SSM框架系列學(xué)習(xí)總結(jié)2之Spring AOP

先整理AOP之前, 我先把之前DI的內(nèi)容整理完!

DI

Spring的依賴注入第二種方式:

通過構(gòu)造方法注入屬性
首先提供一個(gè)實(shí)體類, 不過這個(gè)類沒有屬性的get和set方法, 只有一個(gè)有參的構(gòu)造方法和toString方法.

public class Boy {
    private String id;
    private String name;
    private Integer age;
    private Double salary;

    public Boy(String id, String name, Integer age, Double salary) {
        System.out.println("IOC容器創(chuàng)建對(duì)象");
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Boy{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}

Spring配置

    <!--
        通過constructor實(shí)現(xiàn)依賴注入
        constructor-arg: 通過構(gòu)造方法傳過去的值
        type: 用來限制傳遞的參數(shù)的類型
    -->
    <bean id="boyId" class="com.wtu.spring.di.constructor.Boy">
        <!--
        <constructor-arg type="java.lang.String">
            <value>001</value>
        </constructor-arg>
        <constructor-arg type="java.lang.String">
            <value>小陽</value>
        </constructor-arg>
        <constructor-arg type="java.lang.Integer">
            <value>16</value>
        </constructor-arg>
        <constructor-arg type="java.lang.Double">
            <value>1000</value>
        </constructor-arg>
        -->

        <!-- index: 參數(shù)在構(gòu)造方法中的位置, 第一個(gè)參數(shù)從0開始 -->
        <constructor-arg index="0">
            <value>001</value>
        </constructor-arg>
        <constructor-arg index="1">
            <value>小陽</value>
        </constructor-arg>
        <constructor-arg index="2">
            <value>16</value>
        </constructor-arg>
        <constructor-arg index="3">
            <value>1000</value>
        </constructor-arg>
    </bean>

測(cè)試類:

        // 啟動(dòng)IOC容器
        ApplicationContext ac = new ClassPathXmlApplicationContext(
                new String[]{"com/wtu/spring/di/constructor/spring3.0.xml"}
        );

        Boy boy = (Boy) ac.getBean("boyId");
        System.out.println(boy);
        //Boy{id='001', name='小陽', age=16, salary=1000.0}

注入Date類型:
在依賴注入中 如何將一個(gè)字符串轉(zhuǎn)換成Date對(duì)象, 注入到目標(biāo)對(duì)象

public class Boy {
    private String id;
    private String name;
    private Integer age;
    private Date birthday;
    private Double salary;

    public Boy() {
        System.out.println("Ioc容器創(chuàng)建對(duì)象");
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Double getSalary() {
        return salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Boy{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", birthday=" + birthday +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}

Spring配置

    <!-- 注冊(cè)時(shí)間格式轉(zhuǎn)換器
        -->
    <bean id="format" class="java.text.SimpleDateFormat">
        <constructor-arg>
            <!-- 時(shí)間的格式樣式 -->
            <value>yyyy-MM-dd</value>
        </constructor-arg>
    </bean>

    <!-- 注冊(cè)bean對(duì)象
        new SimpleDateFormat("yyyy-MM-dd").parse("2017-12-12"); 
    -->
    <bean id="boy" class="com.wtu.spring.di.set.Boy">
        <property name="birthday">
            <bean factory-bean="format" factory-method="parse">
                <constructor-arg>
                    <value>2017-12-12</value>
                </constructor-arg>
            </bean>
        </property>
    </bean>

測(cè)試類:

        // 啟動(dòng)IOC容器
        ApplicationContext ac = new ClassPathXmlApplicationContext(
                new String[]{"com/wtu/spring/di/set/spring3.0.xml"}
        );

        Boy boy = (Boy) ac.getBean("boy");
        System.out.println(new SimpleDateFormat("yyyy-MM-dd").parse("2017-12-12"));
        //Tue Dec 12 00:00:00 CST 2017
        System.out.println(boy);
        //Boy{id='null', name='null', birthday=Tue Dec 12 00:00:00 CST 2017, age=null, salary=null}
DI注解形式
結(jié)構(gòu).png

UserDaoImpl.java:

public class UserDaoImpl implements UserDao {
    public UserDaoImpl() {
        System.out.println("IOC容器創(chuàng)建對(duì)象");
    }

    @Override
    public void addUser() {
        System.out.println("添加用戶");
    }
}

UserServiceImpl.java:

public class UserServiceImpl implements UserService {
    private UserDao userDao;

    public UserServiceImpl() {
        System.out.println("IOC容器創(chuàng)建UserServiceImpl對(duì)象");
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void addUser() {
        System.out.println("service中addUser方法被調(diào)用了");
    }
}

Spring配置:

    <!--注冊(cè)UserDao對(duì)象 -->
    <bean id="userDao" class="com.wtu.spring.di.annotation.UserDaoImpl"/>

    <!--注冊(cè)UserService對(duì)象 -->
    <bean id="userService" class="com.wtu.spring.di.annotation.UserServiceImpl">
        <property name="userDao" ref="userDao"/>
    </bean>

測(cè)試類:

        // 啟動(dòng)IOC容器
        ApplicationContext ac = new ClassPathXmlApplicationContext(
                new String[]{"com/wtu/spring/di/annotation/spring3.0.xml"}
        );

        UserService us = (UserService) ac.getBean("userService");

運(yùn)行結(jié)果:


結(jié)果.png

注解改寫:
后來的Spring配置文件:

<?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:context="http://www.springframework.org/schema/context"

       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 開啟Spring注解的功能 -->
    <context:annotation-config/>
    <!-- 讓springIOC容器自動(dòng)去掃描某一個(gè)包, 創(chuàng)建該包下的bean對(duì)象 -->
    <context:component-scan base-package="com.wtu.spring.di.annotation2"/>

</beans>

UserDaoImpl.java:

/**
 * @Component(value = "userDao")
 *  告訴SpringIOC容器創(chuàng)建一個(gè)id為userDao的bean對(duì)象
 *  value可以省略, 但是此時(shí)生成的id為類名, 并且首字母小寫
 */
//@Component(value = "userDao") // userDaoImpl
@Repository(value = "userDao")
public class UserDaoImpl implements UserDao {
    public UserDaoImpl() {
        System.out.println("IOC容器創(chuàng)建對(duì)象");
    }

    @Override
    public void addUser() {
        System.out.println("添加用戶");
    }
}

UserServiceImpl.java:

@Service(value = "userServiceImpl")
public class UserServiceImpl implements UserService {
    @Resource(name = "userDao")
//    @Autowired
    private UserDao userDao;

    public UserServiceImpl() {
        System.out.println("IOC容器創(chuàng)建UserServiceImpl對(duì)象");
    }

    /*
        spring會(huì)自動(dòng)查找id為userDao的bean對(duì)象, 如果找到就通過下面的set方法注入進(jìn)來
     */
//    @Resource(name = "userDao")
//    public void setUserDao(UserDao userDao) {
//        this.userDao = userDao;
//    }

    @Override
    public void addUser() {
        System.out.println("service中addUser方法被調(diào)用了");
    }
}

測(cè)試類:

        // 啟動(dòng)IOC容器
        ApplicationContext ac = new ClassPathXmlApplicationContext(
                new String[]{"com/wtu/spring/di/annotation2/spring3.0.xml"}
        );

        UserService us = (UserService) ac.getBean("userServiceImpl");
        us.addUser();

運(yùn)行結(jié)果:

結(jié)果.png

注意: @Component現(xiàn)在不提倡使用
對(duì)于注冊(cè)bean對(duì)象的注解現(xiàn)在提倡使用如下三個(gè):
@Controller 一般作用在springMVC中
@Service 作用在業(yè)務(wù)層
@Repository 作用在持久層
但是不是絕對(duì)的, 任何一個(gè)在任何一層都可以用

AOP

AOP.jpeg
AOP.jpeg
AOP.jpeg

如圖上述的解決方案不好:
在javaweb三層框架中辆飘,dao層是數(shù)據(jù)訪問層,它的核心業(yè)務(wù)就是操作數(shù)據(jù)庫栅螟,然而像輸出時(shí)間,開啟事務(wù), 日志輸出等等。這些事情都不是核心業(yè)務(wù),我們可以稱之為服務(wù)代碼毁涉,我們不能將執(zhí)行服務(wù)代碼這樣的事情交給dao層來做。但是我們又需要這樣的服務(wù)代碼锈死。所以我們可以找該類的一個(gè)代理對(duì)象來實(shí)現(xiàn)贫堰。

動(dòng)態(tài)代理圖解:


動(dòng)態(tài)代理圖解.png
動(dòng)態(tài)代理核心代碼模擬實(shí)現(xiàn)

日志類, 切面:

public class MyLog {
    @SuppressWarnings("deprecation")
    public void writeConsole() {
        System.out.println(new Date().toLocaleString());
    }
}

目標(biāo)對(duì)象所在類, 執(zhí)行核心代碼:

public class UserDaoImpl implements UserDao {

    @Override
    public void addUser() {
        System.out.println("添加用戶...");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void deleteUser() {
        System.out.println("刪除用戶");
    }
}

模擬JDK動(dòng)態(tài)代理的Proxy類:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 中間類, 相當(dāng)于JDK動(dòng)態(tài)代理中的Proxy
 * @Author menglanyingfei
 * @Created on 2018.01.17 16:51
 */
public class Middle {
    private UserDao userDao = new UserDaoImpl();
    // 定義切面
    private MyLog myLog = new MyLog();

    public Object getObject() {
        /*
        第一個(gè)參數(shù): 當(dāng)前類的類加載器
        第二個(gè)參數(shù): 代理對(duì)象實(shí)現(xiàn)的接口類型
        第三個(gè)參數(shù): 是個(gè)接口, 在這個(gè)接口中具體去處理如何調(diào)用切面以及目標(biāo)對(duì)象
            匿名內(nèi)部類
         */
        return Proxy.newProxyInstance(
                this.getClass().getClassLoader(),
                userDao.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     *
                     * @param proxy 目標(biāo)對(duì)象
                     * @param method 需要執(zhí)行目標(biāo)對(duì)象的方法對(duì)應(yīng)的Method對(duì)象
                     * @param args 執(zhí)行目標(biāo)對(duì)象業(yè)務(wù)方法時(shí)需要的參數(shù)
                     * @return
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 定義返回值, 目標(biāo)對(duì)象方法執(zhí)行以后的返回值
                        Object result = "";
                        // 獲取目標(biāo)對(duì)象的方法名
                        String methodName = method.getName();
                        if ("addUser".equals(methodName)) {
                            myLog.writeConsole();
                            result = method.invoke(userDao, args);
                            myLog.writeConsole();
                        } else {
                            result = method.invoke(userDao, args);
                        }
                        return result;
                    }
                }
        );
    }

}

測(cè)試類:

        // 通過一個(gè)中間類來獲取代理對(duì)象
        Middle middle = new Middle();
        UserDao userDao = (UserDao) middle.getObject();
        userDao.addUser();


        System.out.println(userDao);
        //com.wtu.spring.aop.base.UserDaoImpl@5305068a
        // 紅色標(biāo)識(shí)顯示
        System.err.println(userDao.getClass());
        // 使用JDK動(dòng)態(tài)代理產(chǎn)生的代理對(duì)象
        //class com.sun.proxy.$Proxy0

運(yùn)行結(jié)果:


運(yùn)行結(jié)果.png

導(dǎo)入aop的所需jar包:


jar.png
SpringAOP的五種通知方式

通知就是告訴代理在什么時(shí)候去執(zhí)行服務(wù)代碼。
1.前置通知: 執(zhí)行目標(biāo)對(duì)象業(yè)務(wù)方法之前執(zhí)行服務(wù)代碼(切面)
2.后置通知: 執(zhí)行目標(biāo)對(duì)象業(yè)務(wù)方法之后執(zhí)行服務(wù)代碼
3.方法正常返回通知:
4.方法拋出異常通知:
5.環(huán)繞通知:

由于晚上太想睡覺了, 所以只先總結(jié)到前置通知, 把剩下的內(nèi)容留到下次總結(jié)吧!
先弄一下前置通知吧!
先定義一個(gè)切面: 輸出日志時(shí)間類

package com.wtu.spring.aop.before;

import java.util.Date;

/**
 * 切面
 * @Author menglanyingfei
 * @Created on 2018.01.17 17:06
 */
public class MyLog {
    @SuppressWarnings("deprecation")
    public void writeConsole() {
        System.out.println(new Date().toLocaleString());
    }
}

目標(biāo)對(duì)象所在類的核心業(yè)務(wù)代碼:

public class UserDaoImpl implements UserDao {

    @Override
    public void addUser() {

//        if (true) {
//            throw new RuntimeException("測(cè)試一下遇到異常執(zhí)行情況");
//        }

        System.out.println("添加用戶...");
    }

    @Override
    public void deleteUser() {
        System.out.println("刪除用戶");
    }
}

Spring配置:

<?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-3.0.xsd

           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd

           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
           ">

    <!-- 注冊(cè)UserDao對(duì)象 -->
    <bean id="userDao" class="com.wtu.spring.aop.before.UserDaoImpl">
    </bean>

    <!-- 配置切面 -->
    <bean id="myLog" class="com.wtu.spring.aop.before.MyLog"/>

    <!--
        aop:config: 配置代理對(duì)象
        proxy-target-class: 指底層的動(dòng)態(tài)代理使用的是JDK, 還是CGLIB代理;
            true表示CGLIB代理, CGLIB代理不是說沒有接口, spring會(huì)根據(jù)目標(biāo)對(duì)象自動(dòng)生成一個(gè)接口
        aop:pointcut: 配置切入點(diǎn)
        execution: 確定切入點(diǎn)的方法
        aop:aspect: 配置切面如何去切切入點(diǎn)
        pointcut-ref: 切面中的方法去切哪個(gè)目標(biāo)對(duì)象中的切入點(diǎn)
    -->
    <aop:config proxy-target-class="true">
        <aop:pointcut expression="execution(* com.wtu.spring.aop.before.UserDaoImpl.a*(..))" id="xxx"/>

        <aop:aspect ref="myLog">
            <aop:before method="writeConsole" pointcut-ref="xxx"/>
        </aop:aspect>
    </aop:config>

</beans>

測(cè)試類:

package com.wtu.spring.aop.before;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Author menglanyingfei
 * @Created on 2018.01.17 14:22
 */
public class TestSpringIoc {
    public static void main(String[] args) {
        // 啟動(dòng)Ioc容器
        ApplicationContext ac = new ClassPathXmlApplicationContext(
                new String[]{"com/wtu/spring/aop/before/spring3.0.xml"}
        );

        UserDao userDao = (UserDao) ac.getBean("userDao");
        userDao.addUser();

        System.out.println(userDao.getClass());
        //class com.wtu.spring.aop.before.UserDaoImpl$$EnhancerByCGLIB$$aa48c241
    }
}

運(yùn)行結(jié)果:


運(yùn)行結(jié)果.png

完整代碼地址見Github

https://github.com/menglanyingfei/SSMLearning/tree/master/spring_day02

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末待牵,一起剝皮案震驚了整個(gè)濱河市其屏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌缨该,老刑警劉巖偎行,帶你破解...
    沈念sama閱讀 222,807評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異压彭,居然都是意外死亡睦优,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門壮不,熙熙樓的掌柜王于貴愁眉苦臉地迎上來汗盘,“玉大人,你說我怎么就攤上這事询一∫酰” “怎么了癌椿?”我有些...
    開封第一講書人閱讀 169,589評(píng)論 0 363
  • 文/不壞的土叔 我叫張陵,是天一觀的道長菱阵。 經(jīng)常有香客問我踢俄,道長,這世上最難降的妖魔是什么晴及? 我笑而不...
    開封第一講書人閱讀 60,188評(píng)論 1 300
  • 正文 為了忘掉前任都办,我火速辦了婚禮,結(jié)果婚禮上虑稼,老公的妹妹穿的比我還像新娘琳钉。我一直安慰自己,他們只是感情好蛛倦,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評(píng)論 6 398
  • 文/花漫 我一把揭開白布歌懒。 她就那樣靜靜地躺著,像睡著了一般溯壶。 火紅的嫁衣襯著肌膚如雪及皂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,785評(píng)論 1 314
  • 那天且改,我揣著相機(jī)與錄音验烧,去河邊找鬼。 笑死钾虐,一個(gè)胖子當(dāng)著我的面吹牛噪窘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播效扫,決...
    沈念sama閱讀 41,220評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼倔监,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了菌仁?” 一聲冷哼從身側(cè)響起浩习,我...
    開封第一講書人閱讀 40,167評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎济丘,沒想到半個(gè)月后谱秽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,698評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡摹迷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評(píng)論 3 343
  • 正文 我和宋清朗相戀三年疟赊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片峡碉。...
    茶點(diǎn)故事閱讀 40,912評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡近哟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鲫寄,到底是詐尸還是另有隱情吉执,我是刑警寧澤疯淫,帶...
    沈念sama閱讀 36,572評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站戳玫,受9級(jí)特大地震影響熙掺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜咕宿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評(píng)論 3 336
  • 文/蒙蒙 一币绩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧荠列,春花似錦类浪、人聲如沸载城。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽诉瓦。三九已至川队,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間睬澡,已是汗流浹背固额。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評(píng)論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留煞聪,地道東北人斗躏。 一個(gè)月前我還...
    沈念sama閱讀 49,359評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像昔脯,于是被迫代替她去往敵國和親啄糙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評(píng)論 2 361

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理云稚,服務(wù)發(fā)現(xiàn)隧饼,斷路器,智...
    卡卡羅2017閱讀 134,716評(píng)論 18 139
  • 一> 代理模式 概述 代理(Proxy)是一種設(shè)計(jì)模式静陈, 提供了對(duì)目標(biāo)對(duì)象另外的訪問方式燕雁;即通過代理訪問目標(biāo)對(duì)象。...
    奮斗的老王閱讀 1,123評(píng)論 0 50
  • 開始使用Spring Framework 5.0和設(shè)計(jì)模式 介紹Spring框架 Spring簡化了應(yīng)用程序開發(fā)鲸拥,...
    一顆懶能閱讀 2,875評(píng)論 3 21
  • 這兩天忙的沒有午休拐格,從上午到晚上,就圍著一件事而忙碌刑赶,可是跟著別人的思路走著捏浊,走著,自己發(fā)現(xiàn)了有什么不太對(duì)勁角撞,卻沒...
    cc08閱讀 327評(píng)論 0 0
  • 老師,是培訓(xùn)機(jī)構(gòu)不可或缺的一部分热康,與培訓(xùn)機(jī)構(gòu)的利潤有直接聯(lián)系沛申。但是,作為老師姐军,卻不能大意铁材,因?yàn)槔习遄畈幌矚g以下幾種...
    時(shí)間寶閱讀 428評(píng)論 0 0