spring AOP

一在抛、AOP概述

1.AOP
全稱是Aspect Oriented Programming即:面向切面編程。

簡單的說它就是把程序重復(fù)的代碼抽取出來谦炒,在需要執(zhí)行的時候挫剑,使用動態(tài)代理的技術(shù)去扣,在不修改源碼的基礎(chǔ)上,對我們的已有方法進(jìn)行增強(qiáng)樊破。

2.作用
在程序運(yùn)行期間愉棱,不修改源碼對已有方法進(jìn)行增強(qiáng)唆铐。
①減少重復(fù)代碼
②提高開發(fā)效率
③維護(hù)方便

AOP的實(shí)現(xiàn)方式
使用動態(tài)代理技術(shù)

二、動態(tài)代理

1.動態(tài)代理的特點(diǎn)
字節(jié)碼隨用隨創(chuàng)建奔滑,隨用隨加載或链。
它與靜態(tài)代理的區(qū)別也在于此。因?yàn)殪o態(tài)代理是字節(jié)碼一上來就創(chuàng)建好档押,并完成加載澳盐。
裝飾者模式就是靜態(tài)代理的一種體現(xiàn)。

2.動態(tài)代理常用的兩種方式

  • 基于接口的動態(tài)代理
    提供者:JDK官方的Proxy類令宿。
    要求:被代理類最少實(shí)現(xiàn)一個接口叼耙。
    創(chuàng)建代理對象的方法:newProxyInstance(ClassLoader,Class[],InvocationHandler)
    參數(shù)的含義:
    ClassLoader:類加載器粒没。和被代理對象使用相同的類加載器筛婉。一般都是固定寫法
    Class[]:字節(jié)碼數(shù)組。被代理類實(shí)現(xiàn)的接口癞松。要求代理對象和被代理對象有相同的行為爽撒。一般都是固定寫法。
    InvocationHandler:它是一個接口响蓉,就是用于提供增強(qiáng)代碼的硕勿。一般都是寫一個該接口的實(shí)現(xiàn)類。實(shí)現(xiàn)類可以是匿名內(nèi)部類枫甲。
    含義:如何代理源武。這個參數(shù)的代碼只能是誰用誰提供。
    策略模式:數(shù)據(jù)有想幻,目標(biāo)明確粱栖,達(dá)成目標(biāo)的過程就是策略。

舉一個演員和經(jīng)紀(jì)公司的例子

定義一個藝人標(biāo)準(zhǔn)的接口IActor.java

package com.company.proxy;

/**
 * 藝人標(biāo)準(zhǔn)
 */
public interface IActor {

    public void basicAct(float money);

    public void dangerAct(float money);

}

再實(shí)現(xiàn)一個演員類Actor.java

package com.company.proxy;
/**
 * 一個演員
 */
public class Actor implements IActor{
    public void basicAct(float money){
        System.out.println("拿到錢脏毯,開始基本表演"+money);
    }

    public void dangerAct(float money){
        System.out.println("拿到錢闹究,開始危險表演"+money);
    }
}

最后再來個經(jīng)紀(jì)公司管理演員的出場費(fèi)用,并使用動態(tài)代理
Client.java

package com.company.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 模擬一個經(jīng)紀(jì)公司
 */
public class Client {
    public static void main(String[] args) {
        Actor actor = new Actor();
//沒有經(jīng)紀(jì)公司的時候食店,直接找演員渣淤。
//        actor.basicAct(100f);
//        actor.dangerAct(500f);
        //動態(tài)代理
        IActor proxyActor = (IActor) Proxy.newProxyInstance(actor.getClass().getClassLoader(),
                actor.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 執(zhí)行被代理對象的任何方法都會經(jīng)過該方法,該方法有攔截的功能
                     * 方法的參數(shù)
                     *      Object proxy:代理對象的引用叛买。不一定每次都會有
                     *      Method method:當(dāng)前執(zhí)行的方法
                     *      Object[] args:當(dāng)前執(zhí)行方法所需的參數(shù)
                     * 返回值:
                     *      當(dāng)前執(zhí)行方式的返回值
                     */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object rtValue = null;
                //1.取出執(zhí)行方法中的參數(shù)
                Float money = (Float) args[0];
                //2.判斷當(dāng)前執(zhí)行的是什么方法
                //每個經(jīng)紀(jì)公司對不同演出收費(fèi)不一樣性芬,此處開始判斷
                if ("basicAct".equals(method.getName())){
                    //基本演出
                    if (money > 10000){
                        //執(zhí)行方法
                        //看上去劇組是給了8000褪那,實(shí)際到演員手里只有4000
                      //這就是我們沒有修改原來basicAct方法源碼镣奋,對方法進(jìn)行了增強(qiáng)
                        rtValue = method.invoke(actor,money/2);
                    }
                }
                if ("dangerAct".equals(method.getName())){
                    //危險演出
                    if (money > 50000){
                        //執(zhí)行方法
                        rtValue = method.invoke(actor,money);
                    }
                }
                return rtValue;
            }
        });
        //劇組無法直接聯(lián)系演員推姻,而是由經(jīng)紀(jì)公司找的演員
        proxyActor.basicAct(80000f);
        proxyActor.dangerAct(60000f);
    }
}
運(yùn)行結(jié)果
  • 基于子類的動態(tài)代理
    提供者:第三方的CGLib哪轿,如果報asmxxxx異常,需要導(dǎo)入asm.jar华烟。
    要求:被代理類不能用final修飾的類(最終類)坑鱼。
    涉及的類:Enhancer
    創(chuàng)建代理對象的方法:create(Class,Callback);
    參數(shù)的含義:
    Class:被代理對象的字節(jié)碼
    Callback:如何代理。它和InvocationHandler的作用一樣动漾。
    它也是一個接口丁屎,一般使用該接口的子接口MethodInterceptor,在使用是也是創(chuàng)建該接口的匿名內(nèi)部類旱眯。

還是上述例子
不需要IActor.java的接口

更改Client.java

package com.company.proxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * 模擬一個經(jīng)紀(jì)公司
 */
public class Client {
    public static void main(String[] args) {
        Actor actor = new Actor();
        //動態(tài)代理

       Actor cglibActor = (Actor) Enhancer.create(actor.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                /**
                 * 執(zhí)行被代理對象的任何方法都會經(jīng)過該方法晨川。它和基于接口動態(tài)代理的invoke方法的作用一樣
                 * 方法的參數(shù):
                 *      前面三個和invoke方法的參數(shù)含義和作用一樣。
                 *      MethodProxy methodProxy:當(dāng)前執(zhí)行方法的代理對象删豺,一般不用
                 */
                Object rtValue = null;
                //1.取出執(zhí)行方法中的參數(shù)
                Float money = (Float) objects[0];
                //2.判斷當(dāng)前執(zhí)行的是什么方法
                //每個經(jīng)紀(jì)公司對不同演出收費(fèi)不一樣共虑,此處開始判斷
                if ("basicAct".equals(method.getName())){
                    //基本演出
                    if (money > 10000){
                        //執(zhí)行方法
                        //看上去劇組是給了8000,實(shí)際到演員手里只有4000
                        //這就是我們沒有修改原來basicAct方法源碼呀页,對方法進(jìn)行了增強(qiáng)
                        rtValue = method.invoke(actor,money/2);
                    }
                }
                if ("dangerAct".equals(method.getName())){
                    //危險演出
                    if (money > 50000){
                        //執(zhí)行方法
                        rtValue = method.invoke(actor,money);
                    }
                }
                return rtValue;
            }
        });
            cglibActor.basicAct(50000);
            cglibActor.dangerAct(100000);
    }
}
運(yùn)行結(jié)果

3.動態(tài)代理的應(yīng)用——連接池原理(c3p0)

導(dǎo)入的jar包要mysql-connector-java-5.0.8-bin.jar因?yàn)檫@個包里的Connection是個類妈拌,而5.1.7的版本里Connection是個接口,無法強(qiáng)轉(zhuǎn)蓬蝶。

沒有動態(tài)代理之前

關(guān)閉一個連接就少一個尘分,啟用動態(tài)代理,讓用完的連接還回池中

項(xiàng)目目錄結(jié)構(gòu)

項(xiàng)目目錄結(jié)構(gòu)

dbconfig.properties

#數(shù)據(jù)庫連接的配置

#數(shù)據(jù)庫驅(qū)動
DRIVER=com.mysql.jdbc.Driver

#連接字符串
URL=jdbc:mysql://localhost:3306/goods

#數(shù)據(jù)庫用戶名
USER=root

#數(shù)據(jù)庫密碼
PASSWORD=root

JDBCUtil.java

package com.edu.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ResourceBundle;

/**
 * 數(shù)據(jù)庫操作相關(guān)的工具類
 * @author zhy
 *
 */
public class JDBCUtil {

    //使用ResourceBundle讀取資源文件
    private static ResourceBundle bundle = ResourceBundle.getBundle("dbconfig");
    private static String driver;
    private static String url;
    private static String user;
    private static String password;

    //使用靜態(tài)代碼塊進(jìn)行賦值
    static{
        driver = bundle.getString("DRIVER");
        url = bundle.getString("URL");
        user = bundle.getString("USER");
        password = bundle.getString("PASSWORD");
    }

    /**
     * 獲取連接
     * @return
     */
    public static Connection getConnection(){
        Connection conn = null;
        try {
            Class.forName(driver);
            conn = DriverManager.getConnection(url, user, password);
            return conn;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 釋放資源
     * @param rs
     * @param st
     * @param conn
     */
    public static void release(ResultSet rs,Statement st,Connection conn){
        if(rs != null){
            try{
                rs.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
        if(st != null){
            try{
                st.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
        if(conn != null){
            try{
                conn.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
}

自定義連接池MyDataSource.java

package com.edu.dataSource;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.edu.util.JDBCUtil;

/**
 * 自定義連接池
 * @author zhy
 *
 */
public class MyDataSource {
    //定義一個池丸氛,用于存放連接
    private static List<Connection> pool = Collections.synchronizedList(new ArrayList<Connection>());//把ArrayList轉(zhuǎn)成線程安全的

    //使用靜態(tài)代碼塊給池中加入連接
    static{
        for(int i=0;i<10;i++){
            Connection conn = JDBCUtil.getConnection();
            pool.add(conn);
        }
    }

    /**
     * 獲取一個連接
     * @return
     */
    public static Connection getConnection(){
        final Connection conn =  pool.remove(0);
        //創(chuàng)建代理對象
        Connection proxyConn = (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(),
                conn.getClass().getInterfaces(),
                new InvocationHandler() {

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object rtValue = null;
                        //1.判斷當(dāng)前方法是不是close方法
                        if("close".equals(method.getName())){
                            //不能直接關(guān)閉
                            pool.add(conn);//還回池中
                        }else{
                            rtValue = method.invoke(conn, args);
                        }
                        return rtValue;
                    }
                });
        return proxyConn;
    }
    /**
     * 獲取池中的連接數(shù)
     * @return
     */
    public static int getPoolSize(){
        return pool.size();
    }
}

測試類

package com.edu.test;

import com.edu.dataSource.MyDataSource;
import java.sql.Connection;

public class Main {

    public static void main(String[] args) throws Exception {
        int size = MyDataSource.getPoolSize();
        System.out.println("使用連接之前"+size);
        for (int i = 0;i < 8 ;i++) {
            Connection conn = MyDataSource.getConnection();
            System.out.println(conn);
            conn.close();
        }
        int size1 = MyDataSource.getPoolSize();
        System.out.println("使用連接之后"+size1);
        for (int i = 0;i < 8 ;i++) {
            Connection conn = MyDataSource.getConnection();
            System.out.println(conn);
            conn.close();
        }
    }
}
運(yùn)行結(jié)果

三培愁、Spring中的AOP

1.AOP相關(guān)術(shù)語

  • Joinpoint(連接點(diǎn))
    業(yè)務(wù)層的接口方法,一端:公共代碼(增強(qiáng)的代碼)缓窜;另一端就是業(yè)務(wù)竭钝,究竟要干什么事。這個連接點(diǎn)就是將業(yè)務(wù)和增強(qiáng)的代碼結(jié)合起來雹洗。
  • Pointcut(切入點(diǎn)):
    被增強(qiáng)的就是切入點(diǎn)香罐,沒被增強(qiáng)的就是連接點(diǎn)。
    連接點(diǎn)不一定是切入點(diǎn)时肿;切入點(diǎn)一定是連接點(diǎn)庇茫。
  • Advice(通知/增強(qiáng)):
    增強(qiáng)的代碼在哪個類,哪個類就是通知螃成。
    通知類型:前置通知旦签,后置通知,異常通知寸宏,最終通知宁炫,環(huán)繞通知。
  • Introduction(引介):
    引介是一種特殊的通知在不修改類代碼的前提下, Introduction可以在運(yùn)行期為類動態(tài)地添加一些方法或Field氮凝。
  • Target(目標(biāo)對象):
    代理的目標(biāo)對象羔巢。
  • Weaving(織入):
    是指把增強(qiáng)應(yīng)用到目標(biāo)對象來創(chuàng)建新的代理對象的過程。
    spring采用動態(tài)代理織入,而AspectJ采用編譯期織入和類裝載期織入竿秆。
  • Proxy(代理):
    一個類被AOP織入增強(qiáng)后启摄,就產(chǎn)生一個結(jié)果代理類。
  • Aspect(切面):
    是切入點(diǎn)和通知(引介)的結(jié)合幽钢。

2.基于XML的AOP環(huán)境搭建

所需jar包

所需jar包

目錄結(jié)構(gòu)

目錄結(jié)構(gòu)

需要加強(qiáng)的類:

package com.edu.utils;

public class Logger {
    /**
     * 記錄日志的操作
     * 計(jì)劃讓其在業(yè)務(wù)核心方法(切入點(diǎn)方法)執(zhí)行之前執(zhí)行
     */
    /**
     * 前置通知
     */
    public void beforePrintLog(){
        System.out.println("Logger中的beforePrintLog方法開始記錄日志了歉备。。匪燕。蕾羊。");
    }

    /**
     * 后置通知
     */
    public void afterReturningPrintLog(){
        System.out.println("Logger中的afterReturningPrintLog方法開始記錄日志了。帽驯。龟再。。");
    }

    /**
     * 異常通知
     */
    public void afterThrowingPrintLog(){
        System.out.println("Logger中的afterThrowingPrintLog方法開始記錄日志了界拦。吸申。。享甸。");
    }


    /**
     * 最終通知
     */
    public void afterPrintLog(){
        System.out.println("Logger中的afterPrintLog方法開始記錄日志了截碴。。蛉威。日丹。");
    }
}

配置bean.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">
    <!-- 配置Service -->
    <bean id="customerService" class="com.edu.service.impl.CustomerImpl"></bean>
    <!-- 基于XML的aop配置 -->
    <!-- 1.把通知類交給spring管理 -->
    <bean id="Logger" class="com.edu.utils.Logger"></bean>
    <!-- 2.導(dǎo)入aop名稱空間,并且使用aop:config開始aop的配置 -->
    <aop:config>
        <!-- 定義通用的切入點(diǎn)表達(dá)式蚯嫌,如果寫在aop:aspct標(biāo)簽外部哲虾,則表示所有切面可用 -->
        <aop:pointcut expression="execution(* com.edu.service.impl.*.*(..))" id="pt1"/>
        <!-- 3.使用aop:aspect配置切面,id屬性用于給切面提供一個唯一標(biāo)識择示。ref屬性:用于通知bean的id -->
        <aop:aspect id="logAdvice" ref="Logger">
            <!-- 4.配置通知的類型束凑,指定增強(qiáng)的方法什么時候執(zhí)行。
                    method屬性:用于指定的方法名稱栅盲,
                    pointcut:用于指定切入點(diǎn)表達(dá)式
             -->
            <!-- 切入點(diǎn)表達(dá)式:
                    關(guān)鍵字:execution(表達(dá)式)
                    表達(dá)式寫法:
                        訪問修飾符 返回值 報名.報名...類名.方法名(參數(shù)列表)
                    全匹配方法:
                        public void com.edu.service.impl.CustomerImpl.saveCustomer()
                    訪問修飾符可以省略
                        void com.edu.service.impl.CustomerServiceImpl.saveCustomer()
                    返回值可以使用*號汪诉,表示任意返回值
                        * com.edu.service.impl.CustomerServiceImpl.saveCustomer()
                    包名可以使用*號,表示任意包谈秫,但是有幾級包扒寄,需要寫幾個*
                        * *.*.*.*.CustomerServiceImpl.saveCustomer()
                    使用..來表示當(dāng)前包,及其子包
                        * com..CustomerServiceImpl.saveCustomer()
                    類名可以使用*號拟烫,表示任意類
                        * com..*.saveCustomer()
                    方法名可以使用*號该编,表示任意方法
                        * com..*.*()
                    參數(shù)列表可以使用*,表示參數(shù)可以是任意數(shù)據(jù)類型硕淑,但是必須有參數(shù)
                        * com..*.*(*)
                    參數(shù)列表可以使用..表示有無參數(shù)均可课竣,有參數(shù)可以是任意類型
                        * com..*.*(..)
                    全通配方式:
                        * *..*.*(..)
             -->

            <!-- 配置前置通知: 永遠(yuǎn)在切入點(diǎn)方法執(zhí)行之前執(zhí)行 -->
            <aop:before method="beforePrintLog" pointcut-ref="pt1"/>
            <!-- 配置后置通知: 切入點(diǎn)方法正常執(zhí)行之后執(zhí)行 -->
            <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"/>
            <!-- 配置異常通知: 切入點(diǎn)方法執(zhí)行產(chǎn)生異常之后執(zhí)行嘉赎。它和后置通知永遠(yuǎn)只能執(zhí)行一個 -->
            <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"/>
            <!-- 配置最終通知:無論切入點(diǎn)方法是否正常執(zhí)行,它都會在其后面執(zhí)行 -->
            <aop:after method="afterPrintLog" pointcut-ref="pt1"/>

            <!-- 定義通用的切入點(diǎn)表達(dá)式:如果只寫在了aop:aspect標(biāo)簽內(nèi)部稠氮,則表達(dá)式只有當(dāng)前切面可用-->
            <!--<aop:pointcut id="pt1" expression="execution(* com.edu.service.impl.*.*(..))"/>-->

        </aop:aspect>

    </aop:config>
</beans>

ui界面:

package com.edu.ui;

import com.edu.service.ICustomerService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
        ICustomerService cs = (ICustomerService) applicationContext.getBean("customerService");
        cs.saveCustomer();
    }
}
運(yùn)行結(jié)果

環(huán)繞通知

/**
     * 環(huán)繞通知
     *  它是spring框架為我們提供的一種可以在代碼中手動控制增強(qiáng)部分什么時候執(zhí)行的方式曹阔。
     * 問題:
     *  當(dāng)我們配置了環(huán)繞通知之后半开,增強(qiáng)的代碼執(zhí)行了隔披,業(yè)務(wù)核心方法沒有執(zhí)行。
     * 分析:
     *  通過動態(tài)代理我們知道在invoke方法中寂拆,有明確調(diào)用業(yè)務(wù)核心方法:method.invoke()奢米。
     *  我們配置的環(huán)繞通知中,沒有明確調(diào)用業(yè)務(wù)核心方法纠永。
     * 解決:
     *  spring框架為我們提供了一個接口:ProceedingJoinPoint鬓长,它可以作為環(huán)繞通知的方法參數(shù)
     *  在環(huán)繞通知執(zhí)行時,spring框架會為我們提供該接口的實(shí)現(xiàn)類對象尝江,我們直接使用就行涉波。
     *  該接口中有一個方法proceed(),此方法就相當(dāng)于method.invoke()
     */
    public void aroundPringLog(ProceedingJoinPoint pjp){
        try {
            System.out.println("前置通知:Logger類的aroundPringLog方法記錄日志");
            pjp.proceed();
            System.out.println("后置通知:Logger類的aroundPringLog方法記錄日志");
        } catch (Throwable e) {
            System.out.println("異常通知:Logger類的aroundPringLog方法記錄日志");
            e.printStackTrace();
        }finally{
            System.out.println("最終通知:Logger類的aroundPringLog方法記錄日志");
        }
    }

bean.xml

<aop:around method="aroundPrintLog" pointcut-ref="pt1"/>

3.基于注解的AOP環(huán)境搭建
上述案例
加強(qiáng)的類:

package com.edu.utils;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 一個用于記錄日志的類
 *
 */
@Component("logger")
@Aspect//配置了切面
public class Logger {

    @Pointcut("execution(* com.edu.service.impl.*.*(..))")
    private void pt1(){}
    
    /**
     * 前置通知
     */
    //@Before("pt1()")
    public void beforePrintLog(){
        System.out.println("前置:Logger中的beforePrintLog方法開始記錄日志了炭序。啤覆。。惭聂。");
    }
    
    /**
     * 后置通知
     */
    //@AfterReturning("pt1()")
    public void afterReturningPrintLog(){
        System.out.println("后置:Logger中的afterReturningPrintLog方法開始記錄日志了窗声。。辜纲。笨觅。");
    }
    
    /**
     * 異常通知
     */
    //@AfterThrowing("pt1()")
    public void afterThrowingPrintLog(){
        System.out.println("異常:Logger中的afterThrowingPrintLog方法開始記錄日志了。耕腾。见剩。。");
    }
    /**
     * 最終通知
     */
    //@After("pt1()")
    public void afterPrintLog(){
        System.out.println("最終:Logger中的afterPrintLog方法開始記錄日志了扫俺。苍苞。。牵舵。");
    }
    
    
    
    /**
     * 環(huán)繞通知
     */
    @Around("pt1()")
    public Object aroundPrintLog(ProceedingJoinPoint pjp){
        Object rtValue = null;
        try {
            System.out.println("Logger中的aroundPrintLog方法開始記錄日志了柒啤。。畸颅。担巩。前置");
            rtValue = pjp.proceed();
            System.out.println("Logger中的aroundPrintLog方法開始記錄日志了。没炒。涛癌。。后置");
        } catch (Throwable e) {
            System.out.println("Logger中的aroundPrintLog方法開始記錄日志了。拳话。先匪。。異常");
            e.printStackTrace();
        }finally{
            System.out.println("Logger中的aroundPrintLog方法開始記錄日志了弃衍。呀非。。镜盯。最終");
        }
        
        return rtValue;
    }   
}

bean.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"
       xmlns:context="http://www.springframework.org/schema/context"
       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
                           http://www.springframework.org/schema/context 
                           http://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- 配置spring創(chuàng)建容器時要掃描的包 -->
    <context:component-scan base-package="com.edu"></context:component-scan>

    <!-- 開啟spring對注解AOP的支持-->
    <aop:aspectj-autoproxy />
</beans>

純注解

package config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy
public class SpringConfiguration {

}

Client.java

package com.itheima.ui;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.itheima.service.ICustomerService;

import config.SpringConfiguration;

public class Client {

    public static void main(String[] args) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        ICustomerService cs = (ICustomerService) ac.getBean("customerService");
        cs.saveCustomer();
        
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末岸裙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子速缆,更是在濱河造成了極大的恐慌降允,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件艺糜,死亡現(xiàn)場離奇詭異剧董,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)破停,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門翅楼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人辱挥,你說我怎么就攤上這事犁嗅。” “怎么了晤碘?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵褂微,是天一觀的道長。 經(jīng)常有香客問我园爷,道長宠蚂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任童社,我火速辦了婚禮求厕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扰楼。我一直安慰自己呀癣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布弦赖。 她就那樣靜靜地躺著项栏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蹬竖。 梳的紋絲不亂的頭發(fā)上沼沈,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天流酬,我揣著相機(jī)與錄音,去河邊找鬼列另。 笑死芽腾,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的页衙。 我是一名探鬼主播摊滔,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拷姿!你這毒婦竟也來了惭载?” 一聲冷哼從身側(cè)響起旱函,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤响巢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后棒妨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體踪古,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年券腔,在試婚紗的時候發(fā)現(xiàn)自己被綠了伏穆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡纷纫,死狀恐怖枕扫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情辱魁,我是刑警寧澤烟瞧,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站染簇,受9級特大地震影響参滴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜锻弓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一砾赔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧青灼,春花似錦暴心、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至扳躬,卻和暖如春脆诉,著一層夾襖步出監(jiān)牢的瞬間甚亭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工击胜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留亏狰,地道東北人。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓偶摔,卻偏偏與公主長得像暇唾,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子辰斋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 前言 只有光頭才能變強(qiáng) 上一篇已經(jīng)講解了Spring IOC知識點(diǎn)一網(wǎng)打盡策州!,這篇主要是講解Spring的AOP模...
    Java3y閱讀 6,883評論 8 181
  • **** AOP 面向切面編程 底層原理 代理9獭9还摇! 今天AOP課程1藕夫、 Spring 傳統(tǒng) AOP2孽糖、 Spri...
    luweicheng24閱讀 1,365評論 0 1
  • SpringAOP的使用Demo 通過配置管理特性,Spring AOP 模塊直接將面向方面的編程功能集成到了 S...
    獨(dú)念白閱讀 462評論 0 5
  • 0.前言 本文主要想闡述的問題如下:什么動態(tài)代理(AOP)以及如何用JDK的Proxy和InvocationHan...
    SYFHEHE閱讀 2,267評論 1 7
  • 1. Spring AOP介紹 AOP(Aspect-Oriented Programming)毅贮,面向切面的編程办悟,...
    郭尋撫閱讀 1,663評論 1 11