spring ioc aop springmvc springboot的學習

以前沒學spring的時候開發項目是這樣的

  1. 實體類—>class User{ }
  2. daoclass–> UserDao{ … 訪問db}
  3. service—>class UserService{ UserDao userDao = new UserDao();}
  4. actionclass UserAction{UserService userService = new UserService();}

用戶訪問的步驟是
Tomcat->action->service->dao

springIoc
所謂的依賴注入,則是,甲方開放接口,在它需要的時候,能夠講乙方傳遞進來(注入)
所謂的控制反轉,甲乙雙方不相互依賴,交易活動的進行不依賴於甲乙任何一方,整個活動的進行由第三方負責管理。

Spring容器不單單隻有一個,可以歸爲兩種類型
Bean工廠,BeanFactory【功能簡單】
應用上下文,ApplicationContext【功能強大,一般我們使用這個】

講解一下bean是啥?
Java面向對象,對象有方法和屬性,那麼就需要對象實例來調用方法和屬性(即實例化);
凡是有方法或屬性的類都需要實例化,這樣才能具象化去使用這些方法和屬性;

關於bean的註解們:
1、一類是使用Bean,即是把已經在xml文件中配置好的Bean拿來用,完成屬性、方法的組裝;比如@Autowired , @Resource,可以通過byTYPE(@Autowired)、byNAME(@Resource)的方式獲取Bean;

2、一類是註冊Bean,@Component , @Repository , @ Controller , @Service , @Configration這些註解都是把你要實例化的對象轉化成一個Bean,放在IoC容器中,等你要用的時候,它會和上面的@Autowired , @Resource配合到一起,把對象、屬性、方法完美組裝。

示例:
通過Resource獲取BeanFactory
加載Spring配置文件
通過XmlBeanFactory+配置文件來創建IOC容器

在Spring中總體來看可以通過三種方式來配置對象:

使用XML文件配置
使用註解來配置
使用JavaConfig來配置

無參創建對象


/**
 * Created by ozc on 2017/5/10.
 */
public class User {

    private String id;
    private String username;


    public String getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}
        

//idea的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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
<bean id="user" class="User"/>

        // 得到IOC容器對象
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) ac.getBean("user");
        System.out.println(user);

帶參創建對象

    public User(String id, String username) {
        this.id = id;
        this.username = username;
    }
<bean id="user" class="User">

        <!--通過constructor這個節點來指定構造函數的參數類型、名稱、第幾個-->
        <constructor-arg index="0" name="id" type="java.lang.String" value="1"></constructor-arg>
        <constructor-arg index="1" name="username" type="java.lang.String" value="zhongfucheng"></constructor-arg>
    </bean>

注意在constructor上如果構造函數的值是一個對象,而不是一個普通類型的值,我們就需要用到ref屬性了,而不是value屬性,比如說:我在User對象上維護了Person對象的值,想要在構造函數中初始化它。因此,就需要用到ref屬性了

<bean id="person" class="Person"></bean> 
    <bean id="user" class="User" >
        <!--通過constructor這個節點來指定構造函數的參數類型、名稱、第幾個-->
        <constructor-arg index="0" name="id" type="java.lang.String" value="1"></constructor-arg>
        <constructor-arg index="1" name="username" type="java.lang.String" ref="person"></constructor-arg>
    </bean>

配置文件用靜態方法來返回

 <bean id="user" class="Factory" factory-method="getBean" >

    </bean>
public class Factory {

    public static User getBean() {

        return new User();
    }
      
}
//表明該類是配置類
@Configuration

//啓動掃描器,掃描bb包下的
    //也可以指定多個基礎包
    //也可以指定類型
@ComponentScan("bb")
public class AnnotationScan {

}

@ComponentScan掃描器
@Configuration表明該類是配置類
@Component 指定把一個對象加入IOC容器--->@Name也可以實現相同的效果【一般少用】
@Repository 作用同@Component; 在持久層使用
@Service 作用同@Component; 在業務邏輯層使用
@Controller 作用同@Component; 在控制層使用
@Resource 依賴關係
	如果@Resource不指定值,那麼就根據類型來找,相同的類型在IOC容器中不能有兩個
	如果@Resource指定了值,那麼就根據名字來找

在這裏插入圖片描述
singleton【單例】,從IOC容器獲取的對象都是同一個
prototype【多例】,從IOC容器獲取的對象都是不同的

如果我們想要對象在創建後,執行某個方法,我們指定爲init-method屬性就行了。。
如果我們想要IOC容器銷燬後,執行某個方法,我們指定destroy-method屬性就行了。

<bean id="user" class="User" scope="singleton" lazy-init="true" init-method="" destroy-method=""/>

springAop


public class AOP {
    
    public void begin() {
        System.out.println("開始事務");
    }
    public void close() {
        System.out.println("關閉事務");
    }
}

對於上面重複使用的方法,可以封裝起來,用多個時候聲明對象重複使用就可以了

@Repository  //-->這個在Dao層中使用
public class UserDao {


    AOP aop;

    public void save() {

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

    }
    
}

但是這樣還不夠好
不如用以下的代理模式 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>

測試


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>

重複代碼就叫做關注點。

// 保存一個用戶
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);   ////..【關注點代碼】

        } 
   } 

關注點形成的類,就叫切面(類)!


public class AOP {

    public void begin() {
        System.out.println("開始事務");
    }
    public void close() {
        System.out.println("關閉事務");
    }
}

切入點
執行目標對象方法,動態植入切面代碼。可以通過切入點表達式,指定攔截哪些類的哪些方法; 給指定的類在運行的時候植入切面類代碼。
切入點表達式:指定哪些類的哪些方法被攔截
有了Spring,就不需要我們自己寫代理工廠了。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:p="http://www.springframework.org/schema/p"
       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.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">


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

    <!-- 開啓aop註解方式 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

切面類


@Component
@Aspect//指定爲切面類
public class AOP {


    //裏面的值爲切入點表達式
    @Before("execution(* aa.*.*(..))")
    public void begin() {
        System.out.println("開始事務");
    }


    @After("execution(* aa.*.*(..))")
    public void close() {
        System.out.println("關閉事務");
    }
}

UserDao實現了IUser接口


@Component
public class UserDao implements IUser {

    @Override
    public void save() {
        System.out.println("DB:保存用戶");
    }

}
public interface IUser {
    void save();
}

測試


public class App {

    public static void main(String[] args) {

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

        //這裏得到的是代理對象....
        IUser iUser = (IUser) ac.getBean("userDao");

        System.out.println(iUser.getClass());

        iUser.save();
        
    }
}

@Aspect 指定一個類爲切面類
@Pointcut(“execution( cn.itcast.e_aop_anno…*(…))”) 指定切入點表達式
@Before(“pointCut_()”) 前置通知: 目標方法之前執行
@After(“pointCut_()”) 後置通知:目標方法之後執行(始終執行)
@AfterReturning(“pointCut_()”) 返回後通知: 執行方法結束前執行(異常不執行)
@AfterThrowing(“pointCut_()”) 異常通知: 出現異常時候執行
@Around(“pointCut_()”) 環繞通知: 環繞目標方法執行


測試:
    
    // 前置通知 : 在執行目標方法之前執行
    @Before("pointCut_()")
    public void begin(){
        System.out.println("開始事務/異常");
    }
    
    // 後置/最終通知:在執行目標方法之後執行  【無論是否出現異常最終都會執行】
    @After("pointCut_()")
    public void after(){
        System.out.println("提交事務/關閉");
    }
    
    // 返回後通知: 在調用目標方法結束後執行 【出現異常不執行】
    @AfterReturning("pointCut_()")
    public void afterReturning() {
        System.out.println("afterReturning()");
    }
    
    // 異常通知: 當目標方法執行異常時候執行此關注點代碼
    @AfterThrowing("pointCut_()")
    public void afterThrowing(){
        System.out.println("afterThrowing()");
    }
    
    // 環繞通知:環繞目標方式執行
    @Around("pointCut_()")
    public void around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("環繞前....");
        pjp.proceed();  // 執行目標方法
        System.out.println("環繞後....");
    }
  
    
優化
我們的代碼是這樣的:每次寫Before、After等,都要重寫一次切入點表達式,這樣就不優雅了。


    @Before("execution(* aa.*.*(..))")
    public void begin() {
        System.out.println("開始事務");
    }


    @After("execution(* aa.*.*(..))")
    public void close() {
        System.out.println("關閉事務");
    }
    ```

於是乎,我們要使用@Pointcut這個註解,來指定切入點表達式,在用到的地方中,直接引用就行了!
那麼我們的代碼就可以改造成這樣了:

```go
@Component
@Aspect//指定爲切面類
public class AOP {


    // 指定切入點表達式,攔截哪個類的哪些方法
    @Pointcut("execution(* aa.*.*(..))")
    public void pt() {

    }

    @Before("pt()")
    public void begin() {
        System.out.println("開始事務");
    }


    @After("pt()")
    public void close() {
        System.out.println("關閉事務");
    }
}
發佈了49 篇原創文章 · 獲贊 1 · 訪問量 2521
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章