Spring第五篇【cglib、手動實現AOP編程】

前言

到目前爲止,已經簡單學習了Spring的Core模塊、也會怎麼與Struts2框架進行整合了….因而咱們就開啓了Spring的AOP模塊了…在講解AOP模塊以前,首先咱們來說解一下cglib代理、以及怎麼手動實現AOP編程java

cglib代理

在講解cglib以前,首先咱們來回顧一下靜態代理和動態代理….我以前就寫過了靜態代理、動態代理的博文:http://blog.csdn.net/hon_3y/article/details/70655966spring

因爲靜態代理須要實現目標對象的相同接口,那麼可能會致使代理類會很是很是多….很差維護—->所以出現了動態代理編程

動態代理也有個約束:目標對象必定是要有接口的,沒有接口就不能實現動態代理…..—–>所以出現了cglib代理markdown

cglib代理也叫子類代理,從內存中構建出一個子類來擴展目標對象的功能!session

  • CGLIB是一個強大的高性能的代碼生成包,它能夠在運行期擴展Java類與實現Java接口。它普遍的被許多AOP的框架使用,例如Spring AOP和dynaop,爲他們提供方法的interception(攔截)。

編寫cglib代理

接下來咱們就講講怎麼寫cglib代理:app

  • 須要引入cglib – jar文件, 可是spring的核心包中已經包括了cglib功能,因此直接引入spring-core-3.2.5.jar便可。
  • 引入功能包後,就能夠在內存中動態構建子類
  • 代理的類不能爲final,不然報錯【在內存中構建子類來作擴展,固然不能爲final,有final就不能繼承了】
  • 目標對象的方法若是爲final/static, 那麼就不會被攔截,即不會執行目標對象額外的業務方法。
//須要實現MethodInterceptor接口
public class ProxyFactory implements MethodInterceptor{

    // 維護目標對象
    private Object target;
    public ProxyFactory(Object target){
        this.target = target;
    }

    // 給目標對象建立代理對象
    public Object getProxyInstance(){
        //1. 工具類
        Enhancer en = new Enhancer();
        //2. 設置父類
        en.setSuperclass(target.getClass());
        //3. 設置回調函數
        en.setCallback(this);
        //4. 建立子類(代理對象)
        return en.create();
    }


    @Override
    public Object intercept(Object obj, Method method, Object[] args,
            MethodProxy proxy) throws Throwable {

        System.out.println("開始事務.....");

        // 執行目標對象的方法
        Object returnValue = method.invoke(target, args);

        System.out.println("提交事務.....");

        return returnValue;
    }

}
  • 測試:
public class App {

    public static void main(String[] args) {

        UserDao userDao = new UserDao();

        UserDao factory = (UserDao) new ProxyFactory(userDao).getProxyInstance();

        factory.save();
    }
}

這裏寫圖片描述

這裏寫圖片描述

使用cglib就是爲了彌補動態代理的不足【動態代理的目標對象必定要實現接口】框架


手動實現AOP編程

AOP 面向切面的編程:ide

  • AOP能夠實現「業務代碼」與「關注點代碼」分離

下面咱們來看一段代碼:函數

// 保存一個用戶
public void add(User user) { 
        Session session = null; 
        Transaction trans = null; 
        try { 
            session = HibernateSessionFactoryUtils.getSession();   // 【關注點代碼】
            trans = session.beginTransaction();    // 【關注點代碼】

            session.save(user);     // 核心業務代碼

            trans.commit();     //…【關注點代碼】

        } catch (Exception e) {     
            e.printStackTrace(); 
            if(trans != null){ 
                trans.rollback();   //..【關注點代碼】

            } 
        } finally{ 
            HibernateSessionFactoryUtils.closeSession(session);   ////..【關注點代碼】

        } 
   }
  • 關注點代碼,就是指重複執行的代碼。
  • 業務代碼與關注點代碼分離,好處?
    • 關注點代碼寫一次便可
    • 開發者只須要關注核心業務
    • 運行時期,執行核心業務代碼時候動態植入關注點代碼; 【代理】

案例分析:

  • IUser接口
public interface IUser {

    void save();
}

咱們一步一步來分析,首先咱們的UserDao有一個save()方法,每次都要開啓事務和關閉事務工具

//@Component -->任何地方都能用這個
@Repository  //-->這個在Dao層中使用
    public class UserDao {

    public void save() {

        System.out.println("開始事務");
        System.out.println("DB:保存用戶");
        System.out.println("關閉事務");

    }


}
  • 在剛學習java基礎的時候,咱們知道:若是某些功能常常須要用到就封裝成方法:
//@Component -->任何地方都能用這個
@Repository  //-->這個在Dao層中使用
    public class UserDao {

    public void save() {

        begin();
        System.out.println("DB:保存用戶");
        close();

    }

    public void begin() {
        System.out.println("開始事務");
    }
    public void close() {
        System.out.println("關閉事務");
    }
}
  • 如今呢,咱們可能有多個Dao,都須要有開啓事務和關閉事務的功能,如今只有UserDao中有這兩個方法,重用性仍是不夠高。所以咱們抽取出一個類出來
public class AOP {

    public void begin() {
        System.out.println("開始事務");
    }
    public void close() {
        System.out.println("關閉事務");
    }
}
  • 在UserDao維護這個變量,要用的時候,調用方法就好了
@Repository  //-->這個在Dao層中使用
public class UserDao {


    AOP aop;

    public void save() {

        aop.begin();
        System.out.println("DB:保存用戶");
        aop.close();

    }

}
  • 如今的開啓事務、關閉事務仍是須要我在userDao中手動調用。仍是不夠優雅。。我想要的效果:當我在調用userDao的save()方法時,動態地開啓事務、關閉事務。所以,咱們就用到了代理。固然了,真正執行方法的都是userDao、要幹事的是AOP,所以在代理中須要維護他們的引用
public class ProxyFactory {
    //維護目標對象
    private static Object target;

    //維護關鍵點代碼的類
    private static AOP aop;
    public static Object getProxyInstance(Object target_, AOP aop_) {

        //目標對象和關鍵點代碼的類都是經過外界傳遞進來
        target = target_;
        aop = aop_;

        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        aop.begin();
                        Object returnValue = method.invoke(target, args);
                        aop.close();

                        return returnValue;
                    }
                }
        );
    }
}

工廠靜態方法:

  • 把AOP加入IOC容器中
 //把該對象加入到容器中 @Component public class AOP { public void begin() { System.out.println("開始事務"); } public void close() { System.out.println("關閉事務"); } }
  • 把UserDao放入容器中
@Component
public class UserDao {

    public void save() {

        System.out.println("DB:保存用戶");

    }

}
  • 在配置文件中開啓註解掃描,使用工廠靜態方法建立代理對象
<?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:p="http://www.springframework.org/schema/p" 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/context http://www.springframework.org/schema/context/spring-context.xsd">


    <bean id="proxy" class="aa.ProxyFactory" factory-method="getProxyInstance">
        <constructor-arg index="0" ref="userDao"/>
        <constructor-arg index="1" ref="AOP"/>
    </bean>

    <context:component-scan base-package="aa"/>





</beans>
  • 測試,獲得UserDao對象,調用方法

public class App {

    public static void main(String[] args) {

        ApplicationContext ac =
                new ClassPathXmlApplicationContext("aa/applicationContext.xml");


        IUser iUser = (IUser) ac.getBean("proxy");

        iUser.save();



    }
}

這裏寫圖片描述

工廠非靜態方法

上面使用的是工廠靜態方法來建立代理類對象。咱們也使用一下非靜態的工廠方法建立對象

package aa;

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

/** * Created by ozc on 2017/5/11. */

public class ProxyFactory {

    public Object getProxyInstance(final Object target_, final AOP aop_) {

        //目標對象和關鍵點代碼的類都是經過外界傳遞進來

        return Proxy.newProxyInstance(
                target_.getClass().getClassLoader(),
                target_.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        aop_.begin();
                        Object returnValue = method.invoke(target_, args);
                        aop_.close();

                        return returnValue;
                    }
                }
        );
    }
}

配置文件:先建立工廠,再建立代理類對象

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



    <!--建立工廠-->
    <bean id="factory" class="aa.ProxyFactory"/>


    <!--經過工廠建立代理-->
    <bean id="IUser" class="aa.IUser" factory-bean="factory" factory-method="getProxyInstance">
        <constructor-arg index="0" ref="userDao"/>
        <constructor-arg index="1" ref="AOP"/>
    </bean>


    <context:component-scan base-package="aa"/>


</beans>

這裏寫圖片描述

相關文章
相關標籤/搜索