2020-5-23-Spring

SylvesterZhang發表於2024-03-22

簡介、耦合、控制反轉、依賴注入、註解方式反轉控制和依賴注入、Spring整合Junit、銀行轉賬案例、代理、AOP面向切面程式設計、JDBCTemplate

簡介

1核心內容

IOC反向控制、AOP面向切面程式設計

2優勢

方便解耦,簡化程式設計

AOP程式設計支援

宣告式事務支援

方便程式的測試

方便整合各種優秀的框架

降低JAVAEE API的使用難度

JAVA原始碼是經典的學習範例

3四大核心容器

Beans、Core、Context、SpEL


耦合

程式間的依賴關係稱為耦合。解耦就是降低程式間的依賴關係。

1解耦案例一

DriverManager,registeDriver(new com.mysql.jdbc.Driver());
//編譯依賴

class.forName("com.mysql.jdbc.Driver")
//執行依賴
    
//分析:如果刪除掉com.mysql.jdbc.Driver檔案,前者在編譯時報錯,後者在執行時報錯

避免使用new關鍵字,透過讀取配置檔案來獲取要建立物件的全類名,再使用反射來建立物件

2Bean的概念

Bean表示可重複使用的元件

JavaBean表示java語言編寫的重複使用的元件

3工廠模式案例

1)Bean.properties

service1=services.service1

2)BeanFactory.java

package client;

import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;

public class BeanFactory {
    static private Properties properties;
    static private HashMap<String,Object> map;
    static {
        try {
            //讀取配置檔案
            InputStream in=BeanFactory.class.getClassLoader ().getResourceAsStream ("Bean.properties");
            properties=new Properties ();
            properties.load (in);

            //建立例項並放入容器中
            Enumeration keys=properties.keys ();
            map=new HashMap<String, Object> ();
            while (keys.hasMoreElements ()){
                String key=keys.nextElement ().toString ();
                Object obj=Class.forName (properties.getProperty (key)).newInstance ();
                map.put (key,obj);
            }
        } catch (IOException e) {
            e.printStackTrace ( );
        } catch (IllegalAccessException e) {
            e.printStackTrace ( );
        } catch (InstantiationException e) {
            e.printStackTrace ( );
        } catch (ClassNotFoundException e) {
            e.printStackTrace ( );
        }
    }

    public static Object getBean(String beanname){
        return map.get (beanname);
    }
}

3)clientMain

package client;

import services.service1;

public class clientMain {

    public static void main(String[] args) {
        for (int i = 0; i <5 ; i++) {
            service1 s=(service1) BeanFactory.getBean ("service1");
            System.out.println (s);
            s.service ();
        }
    }
}
//執行的是同一個例項的方法

控制反轉

把建立物件的權力交給框架,此過程稱為控制反轉,是框架的重要特性,包括依賴注入、依賴查詢

1Spring IOC的基本使用

1)pom檔案

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.zhanghuan</groupId>
    <artifactId>sprignioc</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <dependency><!--匯入相關依賴-->
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>
    </dependencies>

</project>

2)beans配置檔案

<?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">
    <bean id="service1" class="services.service1"></bean>
</beans>

3)client.java

package client;

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

public class clientMain {

    public static void main(String[] args) {
        ApplicationContext ac=new ClassPathXmlApplicationContext ("beans.xml");
        //建立Bean物件方式一
        service1 s=(service1)ac.getBean ("service1");

        //建立Bean物件方式二
        service1 s1=ac.getBean ("service1",service1.class);

        //總之,透過xml檔案配置的id獲取bean
        
        s.service ();
        s1.service ();
    }
}

2ApplicationContext介面的3種實現類

實現類 傳入配置檔案特點
ClassPathXmlApplicationContext 配置檔案在類路徑下
FileSystemXmlApplicationContext 配置檔案在磁碟任意未知
AnnotationConfigApplicationContext 無配置檔案,配置資訊透過註解設定

3核心容器的介面

介面名 特點
ApplicationContext 讀取完配置檔案,立即建立物件
BeanFactory 讀取完配置檔案,延遲建立物件

常用的是ApplicationContext,可自動根據不同情況實現單例或多例模式

4建立Bean的三種方式

1)預設建構函式建立

<!--省略部分程式碼-->
<bean id="service1" class="services.service1"></bean>

如果物件無預設建構函式,則無法建立例項

2)使用工廠普通方法建立

package services;
//這是工廠類
public class serviceFactory {
    service1 build_service(){
        return new service1 ();
    }
}
<!--省略部分程式碼-->
<bean id="serviceFactory" class="services.serviceFactory"></bean>
<bean id="service_from_factory1" factory-bean="serviceFactory" factory-method="build_service"></bean>

3)使用工廠靜態方法建立

package services;
//這是工廠類
public class serviceStaticFactory {
    static service1 build_service(){
        return new service1 ();
    }
}
<!--省略部分程式碼-->
<bean id="serviceStaticFactory" class="services.serviceStaticFactory" factory-method="build_service"></bean>

5bean標籤的scope屬性

屬性名 說明
singleton 單例
prototype 多例
request 作用於web應用的請求範圍
session 作用於會話的請求範圍
global-session 作用於叢集會話的請求範圍

依賴注入

依賴關係的維護交給spring來做,此過程稱為依賴注入。(建立物件的過程中,將引數傳入,引數可能是其他物件,使得物件間產生依賴)

1可注入的類

基本型別和String型別

其他bean(配置檔案中配置過的或註解配置過的bean)

複合型別/集合型別

注意

經常變化的不要注入

2注入方式

1)構造方法注入

<bean id="student" class="doMain.Student">
        <constructor-arg name="id" value="1"></constructor-arg>
        <constructor-arg name="male" value="男"></constructor-arg>
        <constructor-arg name="name" value="zhanghuan"></constructor-arg>
        <constructor-arg name="math" value="95"></constructor-arg>
        <constructor-arg name="english" value="100"></constructor-arg>
</bean>

需要注意的是,doMain.Student類裡必須要有對應引數的構造方法,否則無法注入,其本質是呼叫構造方法注入

constructor-arg標籤的屬性

屬性 解釋
type 要注入的引數的型別
index 要注入的引數的下標
name 要注入的引數的引數名
value 要注入的引數的值,支援字串和數字
ref 要注入的引數的值,支援配置檔案中的其他bean

2)setter方法注入

<bean id="student" class="doMain.Student">
        <property name="id" value="1"></property>
        <property name="male" value="男"></property>
        <property name="name" value="zhanghuan"></property>
        <property name="math" value="95"></property>
        <property name="english" value="100"></property>
</bean>

property 標籤的屬性

屬性 解釋
name 要注入的引數的引數名
value 要注入的引數的值,支援字串和數字
ref 要注入的引數的值,支援配置檔案中的其他bean

複雜型別透過setter方法注入

package doMain;

import java.util.List;
import java.util.Map;

//目標bean
public class LoveYou {
    private List<Student> students;
    private Map<String,String> map;

    public List<Student> getStudents() {
        return students;
    }

    public void setStudents(List<Student> students) {
        this.students = students;
    }

    public Map<String, String> getMap() {
        return map;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    @Override
    public String toString() {
        return "LoveYou{" +
                "students=" + students +
                ", map=" + map +
                '}';
    }
}

<bean id="loveyou" class="doMain.LoveYou">
        <property name="students">
            <list>
                <ref bean="student"/>
                <ref bean="student"/>
            </list>
        </property>
        <property name="map">
            <map>
                <entry key="name" value="zhanghuan"></entry>
            </map>
        </property>
</bean>

列表型別資料可選標籤:list,array,set

鍵值對資料可選標籤:map,props


註解方式反轉控制和依賴注入

1xml方式和註解方式對比

功能 xml配置檔案 註解
建立物件 bean標籤 Component,Controller,Service,Repository
注入依賴(資料) property標籤 Autowired,Quqlifier,Resource,Value
改變作用範圍 scope屬性 Scope
生命週期相關 init-method屬性 Postconstruct
destroy-method屬性 PreDestroy

2各註解功能

1)Component註解

用於把當前類物件存入spring容器中,屬性value設定該bean的id,如果不設定預設為該類的小寫名。Controller,Service,Repository和該註解的功能一樣。

使用前需要在配置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: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">
    <context:component-scan base-package="doMain"></context:component-scan>
    <!--掃描資料夾,將裡的類例項化後放入容器-->
</beans>

2)注入bean

(1)Autowired註解

指定注入依賴,可配置在類的變數上,也可配置在類的方法上。在變數上配置該註解後,該變數的setter方法可省去。注入的前提是,容器中有唯一一個bean物件和要注入的變數型別匹配,否則導致注入失敗而報錯。

(2)Qualifier註解

不可以單獨使用,必須和Autowored合用,value屬性值設定注入bean的id

(3)Resource註解

可單獨使用,name屬性值設定注入bean的id

注意

以上3中方式只可注入bean型別,無法注入基本型別和String型別,集合型別只能透過xml方式注入

3)注入基本型別和String型別

Value註解

其value屬性值為要注入的基本型別和String型別資料,可以使用spring中的SpEl表示式

4)Scope註解

其value屬性值為singleton和prototype,控制類的使用方式是單例還是多例

5)生命週期相關注解

Postconstruct註解和PreDestroy註解,主要在單例模式下使用,value值為指定的類的構造方法和析構方法

3資料庫增刪改查案例

1)pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.zhanghuan</groupId>
    <artifactId>annotationspring</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>


        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.4</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>

        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>
    </dependencies>

</project>

2)StudentDaoImpl.java

package com.rootpackage.dao;

import com.rootpackage.doMain.Student;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;


import java.sql.SQLException;
import java.util.List;

@Repository("studentdao")
public class StudentDaoImpl implements StudentDao {
    @Autowired
    private QueryRunner runner;

    public void setRunner(QueryRunner runner) {
        this.runner = runner;
    }

    public void insertOne(Student student) {
        try {
            runner.update ("insert into student (male,name,math,english) values(?,?,?,?)",student.getMale (),student.getName (),student.getMath (),student.getEnglish ());
        } catch (SQLException e) {
            e.printStackTrace ( );
        }
    }

    public void deleteOne(int id) {
        try {
            runner.update ("delete from student where id=?",id);
        } catch (SQLException e) {
            e.printStackTrace ( );
        }
    }

    public void updateOne(Student student) {
        try {
            runner.update ("update student set male=?,name=?,math=?,english=? where id=?",student.getMale (),student.getName (),student.getMath (),student.getEnglish (),student.getId ());
        } catch (SQLException e) {
            e.printStackTrace ( );
        }
    }

    public Student findOne(int id) {
        try {
            return runner.query ("select * from student where id=?",new BeanHandler<Student> (Student.class),id);
        } catch (SQLException e) {
            throw new RuntimeException ();
        }
    }

    public List<Student> findAll() {
        try {
            return runner.query ("select * from student",new BeanListHandler<Student> (Student.class));
        } catch (SQLException e) {
            throw new RuntimeException ();
        }
    }
}

3)StudentServiceImpl.java

package com.rootpackage.services;

import com.rootpackage.dao.StudentDao;
import com.rootpackage.doMain.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository("studentserviceimpl")
public class StudentServiceImpl implements StudentService {
    @Autowired
    private StudentDao dao;

    public void insertOne(Student student) {
        dao.insertOne (student);
    }

    public void deleteOne(int id) {
        dao.deleteOne (id);
    }

    public void updateOne(Student student) {
        dao.updateOne (student);
    }

    public Student findOne(int id) {

        return dao.findOne (id);
    }

    public List<Student> findAll() {

        return dao.findAll ();
    }
}

4)beans.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: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">
    <context:component-scan base-package="com.rootpackage"></context:component-scan>
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <constructor-arg name="ds" ref="datasource"></constructor-arg>
    </bean>
    <bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test1?serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=UTF-8"></property>
        <property name="user" value="root"></property>
        <property name="password" value=""></property>
    </bean>
</beans>

5)test01.java


import com.rootpackage.doMain.Student;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.rootpackage.services.StudentServiceImpl;

import java.util.List;


public class test01 {
    private StudentServiceImpl s=null;
    @Before
    public void init(){
        ApplicationContext ac=new ClassPathXmlApplicationContext ("beans.xml");
        s=ac.getBean ("studentserviceimpl",StudentServiceImpl.class);
    }

    @Test
    public void findal(){
        List<Student> list=s.findAll ();
        for (Student i:
                list) {
            System.out.println (i);
        }
    }

    @Test
    public void insertone(){
        Student student=new Student ();
        student.setMale ("男");
        student.setName ("sylvester");
        student.setMath (85);
        student.setEnglish (90);
        s.insertOne (student);
    }

    @Test
    public void deleteone(){
        s.deleteOne (13);
    }

    @Test
    public void updateone(){
        Student student=new Student ();
        student.setId (15);
        student.setMale ("男");
        student.setName ("sylvester1");
        student.setMath (85);
        student.setEnglish (90);
        s.updateOne (student);
    }

    @Test
    public void findone(){
        Student student=s.findOne (15);
        System.out.println (student);
    }
}

4配置類

1)Configuration註解

該註解標誌的類表示是一個配置類

2)ComponentScan註解

指定建立容器掃描的包,value值為要掃描的包的位置

3)Bean註解

修飾方法,該方法的返回值將會被存入spring的ioc容器中

//註解配置方式建立容器
ApplicationContext ac=new AnnotationConfigApplicationContext (springConfig.class,jdbcConfig.class);//這裡可傳入多個位元組碼檔案,傳入的位元組碼檔案對應的類的Configuration註解可省略
s=ac.getBean ("studentserviceimpl",StudentServiceImpl.class);
package config;

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


@Configuration
@ComponentScan("com.rootpackage")
public class springConfig {

}
package config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.beans.PropertyVetoException;

@Configuration
public class jdbcConfig {
    @Bean
    public DataSource createDataSource(){
        try {
            ComboPooledDataSource ds=new ComboPooledDataSource();
            ds.setDriverClass ("com.mysql.jdbc.Driver");
            ds.setJdbcUrl ("jdbc:mysql://127.0.0.1:3306/test1?serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=UTF-8");
            ds.setUser ("root");
            ds.setPassword ("");
            return ds;
        } catch (PropertyVetoException e) {
            throw new RuntimeException (e);
        }
    }

    @Bean
    public QueryRunner createQueryRunner(DataSource ds){
        return new QueryRunner (ds);
    }
}

4)Import註解

在一個配置類中,匯入另一個配置類,一般是父配置類v匯入子配置類。value值為目標配置類的位元組碼檔案

package config;

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


@Configuration
@ComponentScan("com.rootpackage")
@Import (jdbcConfig.class)//這裡將子配置檔案匯入
public class springConfig {

}
ApplicationContext ac=new AnnotationConfigApplicationContext (springConfig.class);//建立容器時只需將父配置檔案的位元組碼檔案匯入即可

5)Scope註解

value值標記例項是單例的還是多例的

6)PropertySource註解

將xxx.properties檔案讀取到spring的環境變數中,在變數上用@Value("${變數名}")可取到變數值

#duid.properties
# 注意:使用者這裡的變數是user,不可用username,username預設是admin無法使用
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test1?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8
user=root
password=
package config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;


@Configuration
@ComponentScan("com.rootpackage")
@Import (jdbcConfig.class)
@PropertySource("classpath:druid.properties")//將properties檔案讀取到spring的環境中
public class springConfig {

}
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.beans.PropertyVetoException;

@Configuration
public class jdbcConfig {
    @Value ("${driverClassName}")//spEl表示式將變數讀取到這裡,並注入給變數
    private  String driverClassName;

    @Value ("${url}")
    private  String url;

    @Value ("${user}")
    private  String user;

    @Value ("${password}")
    private  String password;
    @Bean
    public DataSource createDataSource(){
        try {
            ComboPooledDataSource ds=new ComboPooledDataSource();
            ds.setDriverClass (driverClassName);
            ds.setJdbcUrl (url);
            ds.setUser (user);
            ds.setPassword (password);
            return ds;
        } catch (PropertyVetoException e) {
            throw new RuntimeException (e);
        }
    }

    @Bean
    public QueryRunner createQueryRunner(DataSource ds){
        return new QueryRunner (ds);
    }
}

7)Qualifier註解的另一種用法

package config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.beans.PropertyVetoException;

@Configuration
public class jdbcConfig {
    @Value ("${driverClassName}")
    private  String driverClassName;

    @Value ("${url}")
    private  String url;

    @Value ("${user}")
    private  String user;

    @Value ("${password}")
    private  String password;
    
    @Bean("ds1")
    public DataSource createDataSource(){
        try {
            ComboPooledDataSource ds=new ComboPooledDataSource();
            ds.setDriverClass (driverClassName);
            ds.setJdbcUrl (url);
            ds.setUser (user);
            ds.setPassword (password);
            return ds;
        } catch (PropertyVetoException e) {
            throw new RuntimeException (e);
        }
    }

    @Bean("ds2")
    public DataSource createDataSource1(){
        try {
            ComboPooledDataSource ds=new ComboPooledDataSource();
            ds.setDriverClass (driverClassName);
            ds.setJdbcUrl (url);
            ds.setUser (user);
            ds.setPassword (password);
            return ds;
        } catch (PropertyVetoException e) {
            throw new RuntimeException (e);
        }
    }

    //這裡有兩個datasource分別是ds1和ds2,預設情況下會無法注入而報錯
    @Bean
    public QueryRunner createQueryRunner(@Qualifier("ds1")  DataSource ds){
        //此處的@Qualifier指定將ds1注入,就不會報錯了
        return new QueryRunner (ds);
    }
}


Spring整合Junit

預設情況,測試時需要建立容器,獲取容器中要測試的物件。為了方便,我們想在測試時,也想讓程式自動建立容器且自動注入物件,需要使用Spring的整合Junit的依賴

1)pom.xml中匯入依賴

		<dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

注意兩個依賴的版本,這裡版本不對會報錯

2)測試類上使用註解建立容器和自動注入


import com.rootpackage.doMain.Student;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import com.rootpackage.services.StudentServiceImpl;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;;

import java.util.List;

@RunWith (SpringJUnit4ClassRunner.class)//這個註解表示這個測試類是整合spring的
//@ContextConfiguration(classes=springConfig.class)
@ContextConfiguration(locations = "classpath:beans.xml")//建立容器,將配置檔案匯入,支援兩種配置檔案
public class test01 {
    @Autowired
    private StudentServiceImpl s=null;


    @Test
    public void findal(){
        List<Student> list=s.findAll ();
        for (Student i:
                list) {
            System.out.println (i);
        }
    }

    @Test
    public void insertone(){
        Student student=new Student ();
        student.setMale ("男");
        student.setName ("sylvester");
        student.setMath (85);
        student.setEnglish (90);
        s.insertOne (student);
    }

    @Test
    public void deleteone(){
        s.deleteOne (13);
    }

    @Test
    public void updateone(){
        Student student=new Student ();
        student.setId (15);
        student.setMale ("男");
        student.setName ("sylvester1");
        student.setMath (85);
        student.setEnglish (90);
        s.updateOne (student);
    }

    @Test
    public void findone(){
        Student student=s.findOne (15);
        System.out.println (student);
    }
}


銀行轉賬案例

分析:轉賬過程中涉及事務操作,業務層包含持久層的多個操作,而者多個操作預設情況是多個事務,轉賬過程執行中報錯易導致總資料量發生變化。為解決這個問題,我們使用Threadlocal將資料庫的Connection存放進去,在業務層執行時,直接透過業務層的Threadlocal獲取出Connection,開啟事務進行一些列操作。

1目錄結構

-main
	-java
		-com
            -rootpackage
                -dao
                    --accountDao
                    --accountDaoImpl
                -doMain
                    --Account
                -service
                    --AccountService
                    --AccountServiceImpl
                -utils
                    --ConnectionUtils
                    --TransactionManager
		-config
			--jdbcConfig
			--SpringConfig
	-resources
		--druid.properties
-test
	-java
		--test1

2檔案程式碼

package com.rootpackage.dao;

import com.rootpackage.doMain.Account;

import java.util.List;

public interface accountDao {
    List<Account> findAll();
    void addOme(Account account);
    void deleteOne(int id);
    void updateOne(Account account);
    Account findOne(int id);
}

package com.rootpackage.dao;

import com.rootpackage.doMain.Account;
import com.rootpackage.utils.ConnectionUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.SQLException;
import java.util.List;
@Component
public class accountDaoImpl implements accountDao {
    @Autowired
    private QueryRunner runner;
    @Autowired
    private ConnectionUtils connectionUtils;
    public List<Account> findAll() {
        try {
            return runner.query (connectionUtils.getThreadConnection (),"select * from account",new BeanListHandler<Account> (Account.class));
        } catch (SQLException e) {
            throw new RuntimeException (e);
        }
    }

    public void addOme(Account account) {
        try {
            runner.update (connectionUtils.getThreadConnection (),"insert into account (name,money) values (?,?)",account.getName (),account.getMoney ());
        } catch (SQLException e) {
            throw new RuntimeException (e);
        }
    }

    public void deleteOne(int id) {
        try {
            runner.update (connectionUtils.getThreadConnection (),"delete from account where id=?",id);
        } catch (SQLException e) {
            throw new RuntimeException (e);
        }
    }

    public void updateOne(Account account) {
        try {
            runner.update (connectionUtils.getThreadConnection (),"update account set name=?,money=? where id=?",account.getName (),account.getMoney (),account.getId ());
        } catch (SQLException e) {
            throw new RuntimeException (e);
        }
    }

    public Account findOne(int id) {
        try {
            return runner.query (connectionUtils.getThreadConnection (),"select * from account where id=?",new BeanHandler<Account> (Account.class),id);
        } catch (SQLException e) {
            throw new RuntimeException (e);
        }
    }
}

package com.rootpackage.doMain;

public class Account {
    private int id;
    private String name;
    private int money;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

package com.rootpackage.service;

import com.rootpackage.doMain.Account;

import java.util.List;

public interface AccountService {
    List<Account> findAll();
    void addOme(Account account);
    void deleteOne(int id);
    void updateOne(Account account);
    Account findOne(int id);
    void transfer(int id1,int id2,int money);
}

package com.rootpackage.service;

import com.rootpackage.doMain.Account;
import com.rootpackage.utils.TransactionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import com.rootpackage.dao.accountDaoImpl;

@Component
public class AccountServiceImpl implements AccountService {
    @Autowired
    private accountDaoImpl dao;
    @Autowired
    private TransactionManager manager;

    public List<Account> findAll() {
        try {
            manager.beginTransaction ();
            List<Account> list=dao.findAll ();
            manager.commit ();
            return list;
        } catch (Exception e) {
            manager.rollback ();
            throw new RuntimeException (e);
        } finally {
            manager.release ();
        }
    }

    public void addOme(Account account) {
        try {
            manager.beginTransaction ();
            dao.addOme (account);
            manager.commit ();
        } catch (Exception e) {
            manager.rollback ();
            throw new RuntimeException (e);
        } finally {
            manager.release ();
        }
    }

    public void deleteOne(int id) {
        try {
            manager.beginTransaction ();
            dao.deleteOne (id);
            manager.commit ();
        } catch (Exception e) {
            manager.rollback ();
            throw new RuntimeException (e);
        } finally {
            manager.release ();
        }
    }

    public void updateOne(Account account) {
        try {
            manager.beginTransaction ();
            dao.updateOne (account);
            manager.commit ();
        } catch (Exception e) {
            manager.rollback ();
            throw new RuntimeException (e);
        } finally {
            manager.release ();
        }
    }

    public Account findOne(int id) {
        try {
            manager.beginTransaction ();
            Account a=dao.findOne (id);
            manager.commit ();
            return a;
        } catch (Exception e) {
            manager.rollback ();
            throw new RuntimeException (e);
        } finally {
            manager.release ();
        }
    }

    public void transfer(int id1, int id2, int money) {
        try {
            manager.beginTransaction ();
            Account a1=dao.findOne (id1);
            Account a2=dao.findOne (id2);
            a1.setMoney (a1.getMoney ()-money);
            a2.setMoney (a2.getMoney ()+money);
            dao.updateOne (a1);
            int a=10/0;//這裡會報錯,但是會進行回滾,資料總量不會發生改變
            dao.updateOne (a2);
            manager.commit ();
        } catch (Exception e) {
            manager.rollback ();
            throw new RuntimeException (e);
        } finally {
            manager.release ();
        }
    }
}

package com.rootpackage.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

@Component
public class ConnectionUtils {
    private ThreadLocal<Connection> t1=new ThreadLocal<Connection> ();
    //一個執行緒內部的儲存類,可以在指定執行緒記憶體儲資料,資料儲存以後,只有指定執行緒可以得到儲存資料
    @Autowired
    private DataSource dataSource;

    public Connection getThreadConnection(){
        try {
            Connection con=t1.get ();
            if(con==null){
                con=dataSource.getConnection ();
                t1.set (con);
            }
            return con;
        } catch (SQLException e) {
            throw new RuntimeException (e);
        }
    }
    public void removeConnection(){
        t1.remove ();
    }
}

package com.rootpackage.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.SQLException;

@Component
public class TransactionManager {
    @Autowired
    private ConnectionUtils connectionUtils;

    public void beginTransaction(){
        try {
            connectionUtils.getThreadConnection ().setAutoCommit (false);
        } catch (SQLException e) {
            e.printStackTrace ( );
        }
    }
    public void commit(){
        try {
            connectionUtils.getThreadConnection ().commit ();
        } catch (SQLException e) {
            e.printStackTrace ( );
        }
    }
    public void rollback(){
        try {
            connectionUtils.getThreadConnection ().rollback ();
        } catch (SQLException e) {
            e.printStackTrace ( );
        }
    }
    public void release(){
        connectionUtils.removeConnection ();
    }
}

package config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.beans.PropertyVetoException;

@Configuration
public class jdbcConfig {
    @Value ("${driverClassName}")
    private  String driverClassName;

    @Value ("${url}")
    private  String url;

    @Value ("${user}")
    private  String user;

    @Value ("${password}")
    private  String password;

    @Bean
    public DataSource createdatasource(){
        try {
            ComboPooledDataSource ds=new ComboPooledDataSource();
            ds.setDriverClass (driverClassName);
            ds.setJdbcUrl (url);
            ds.setUser (user);
            ds.setPassword (password);
            return ds;
        } catch (PropertyVetoException e) {
            throw new RuntimeException (e);
        }
    }

    @Bean
    public QueryRunner createqueryrunner(DataSource ds){
        return new QueryRunner (ds);
    }
}

package config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;

@Configuration
@ComponentScan("com.rootpackage")
@PropertySource("classpath:druid.properties")
@Import (jdbcConfig.class)
public class SpringConfig {

}

#duid.properties
# 注意:使用者這裡的變數是user,不可用username,username預設是admin無法使用
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8
user=root
password=
import com.rootpackage.doMain.Account;
import com.rootpackage.service.AccountServiceImpl;
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;
import config.SpringConfig;

import java.util.List;

@RunWith (SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class test1 {
    @Autowired
    private AccountServiceImpl service;
    @Test
    public void findall(){
        List<Account> list=service.findAll ();
        for (Account a:
             list) {
            System.out.println (a);
        }
    }
    @Test
    public void transfer(){

        service.transfer (1,2,500);
    }
}


代理

1jdk中的代理類

java在JDK1.5之後提供了一個"java.lang.reflect.Proxy"類,透過"Proxy"類提供的一個newProxyInstance方法用來建立一個物件的代理物件

//部分程式碼
final computermaker m=new computermaker ();//該被代理物件必須實現一個介面,並且final關鍵字修飾
        saler n=(saler) Proxy.newProxyInstance (m.getClass ( ).getClassLoader ( ), m.getClass ().getInterfaces (), new InvocationHandler ( ) {
            /*
            ClassLoader 類載入器,讓代理物件和被代理物件使用相同的類載入器
            Class[] 位元組碼陣列,讓代理物件和被代理物件有相同的方法
            InvocationHandler 需要建立一個該介面的匿名內部類,並重寫invoke方法以增強
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return method.invoke (m,args);//注意:第一個引數是被代理物件,最後一個引數是該方法傳過來的引數
            }
        });
        n.sale ();

2第三方代理類

由於jdk提供的代理要求被代理物件必須實現介面,對於沒有實現介面的被代理物件可使用第三方代理類

1)pom.xml匯入依賴

		<dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>

2)部分程式碼

final computermaker m=new computermaker ();
        computermaker n=(computermaker)Enhancer.create (m.getClass ( ), new MethodInterceptor ( ) {
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                return method.invoke (m,objects);
            }
        });
        n.sale ();

3轉賬案例改用代理

1)AccountServiceImpl修改簡化

package com.rootpackage.service;

import com.rootpackage.doMain.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import com.rootpackage.dao.accountDaoImpl;

@Component("accountservice")
public class AccountServiceImpl implements AccountService {
    @Autowired
    private accountDaoImpl dao;


    public List<Account> findAll() {
        List<Account> list=dao.findAll ();
        return list;
    }

    public void addOme(Account account) {
        dao.addOme (account);
    }

    public void deleteOne(int id) {
        dao.deleteOne (id);
    }

    public void updateOne(Account account) {
        dao.updateOne (account);
    }

    public Account findOne(int id) {
        Account a=dao.findOne (id);
        return a;
    }

    public void transfer(int id1, int id2, int money) {
        Account a1=dao.findOne (id1);
        Account a2=dao.findOne (id2);
        a1.setMoney (a1.getMoney ()-money);
        a2.setMoney (a2.getMoney ()+money);
        dao.updateOne (a1);
        int a=9/0;
        dao.updateOne (a2);
    }
}

2)新建代理工廠

package com.rootpackage.factory;

import com.rootpackage.service.AccountService;
import com.rootpackage.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
public class BeanFactory {
    @Autowired
    @Qualifier("accountservice")
    private AccountService accountService;
    @Autowired
    private TransactionManager manager;
    public AccountService getAccountService(){
        return (AccountService)Enhancer.create (accountService.getClass ( ), new MethodInterceptor ( ) {
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                Object result=null;
                try {
                    manager.beginTransaction ();
                    result=method.invoke (accountService,objects);
                    manager.commit ();
                    return result;
                } catch (Exception e) {
                    manager.rollback ();
                    throw new RuntimeException (e);
                } finally {
                    manager.release ();
                }
            }
        });
    }
}

3)測試類

import com.rootpackage.doMain.Account;
import com.rootpackage.factory.BeanFactory;
import com.rootpackage.service.AccountServiceImpl;
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;
import config.SpringConfig;

import java.util.List;

@RunWith (SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class test1 {
    @Autowired
    private BeanFactory factory;
    @Test
    public void findall(){
        List<Account> list=factory.getAccountService ().findAll ();//透過代理工廠獲取AccountService物件
        for (Account a:
             list) {
            System.out.println (a);
        }
    }
    @Test
    public void transfer(){

        factory.getAccountService ().transfer (1,2,500);
    }

}


AOP面向切面程式設計

Aspect Oriented Programming,是spring框架中一個重要內容,是函數語言程式設計的一種衍生範型。利用AOP可對業務邏輯各個部分進行隔離,從而使業務邏輯各個部分間耦合度降低。

特點:在程式執行期間,不改變原始碼對已有方法進行增強

優勢:減少重複程式碼,提高開發效率,維護方便

1常用名詞

名詞 英文 解釋
連線點 joinpoint 已經被增強的掛載點,如增強的方法
切入點 pointcut 所有可以被攔截的點(方法)
通知 advice 攔截到切入點後要做的事情
映入 introduction 執行期動態新增的方法或Field
目標物件 target 代理的目標物件
織入 weaving 增強應用到目標物件來建立新的代理物件的過程
代理 proxy 一個類被aop增強後的一個結果類
切面 aspect 切入點和通知的結合

2快速開始

1)pom.xml匯入依賴

		<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>

2)定義一個服務類

package com.rootpackage.services;

public class service1 {
    public void doservice(){
        System.out.println ("開始服務……");
    }
}

3)定義一個日誌類

package com.rootpackage.mout;

public class log {
    public void dolog1(){
        System.out.println ("列印日誌1");
    }
    public void dolog2(){
        System.out.println ("列印日誌2");
    }
    public void dolog3(){
        System.out.println ("列印日誌3");
    }
    public void dolog4(){
        System.out.println ("列印日誌4");
    }
}

4)beans.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 id="service1" class="com.rootpackage.services.service1"></bean>
    <bean id="log" class="com.rootpackage.mout.log"></bean>
    <aop:config>
        <aop:aspect id="asp1" ref="log">
            <!--id是給這個切面的id,ref指向通知類(放入切入點的方法所屬的類)-->
            <aop:before method="dolog1" pointcut="execution(* *..*.doservice(..))"></aop:before>
            <!--method是通知方法的方法名,pointcut切入點-->
            <aop:after-returning method="dolog2" pointcut="execution(* *..*.doservice(..))"></aop:after-returning>
            <aop:after-throwing method="dolog3" pointcut="execution(* *..*.doservice(..))"></aop:after-throwing>
            <aop:after  method="dolog4" pointcut="execution(* *..*.doservice(..))"></aop:after>
        </aop:aspect>
    </aop:config>
</beans>

5)測試類

import com.rootpackage.services.service1;
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(locations ="classpath:beans.xml" )
public class test1 {
    @Autowired
    service1 sv1;

    @Test
   public void test1(){

       sv1.doservice ();
   }
}

3execution指定切點的格式

訪問修飾符 返回值 包名 類名 .方法名(引數型別)
格式 萬用字元 說明
訪問修飾符 *
返回值 *
包名 *,..
類名 *
方法名 *
引數型別 *,.. 直接寫資料型別如int,java.lang.String,可有引數,也可無引數

全通配* *..*.*(..),但通常情況都不用全同配。切入到業務實現類的所有方法,會有一個問題就是通知內容會多執行一次,是由於構造方法導致的。

4四種通知型別

關鍵字 型別
before 前置
after-returning 後置
after-throwing 異常
after 最終

5配置共用切入點

<?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 id="service1" class="com.rootpackage.services.service1"></bean>
    <bean id="log" class="com.rootpackage.mout.log"></bean>
    <aop:config>
        <aop:pointcut id="s1" expression="execution(* * ..*.doservice(..))"></aop:pointcut>
        <aop:aspect id="asp1" ref="log">
            <aop:before method="dolog1" pointcut-ref="s1"></aop:before>
            <aop:after-returning method="dolog2" pointcut-ref="s1"></aop:after-returning>
            <aop:after-throwing method="dolog3" pointcut-ref="s1"></aop:after-throwing>
            <aop:after  method="dolog4" pointcut-ref="s1"></aop:after>
        </aop:aspect>
    </aop:config>
</beans>

6環繞通知

<?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 id="service1" class="com.rootpackage.services.service1"></bean>
    <bean id="log" class="com.rootpackage.mout.log"></bean>
    <aop:config>
        <aop:pointcut id="s1" expression="execution(* * ..*.doservice(..))"></aop:pointcut>
        <aop:aspect id="asp1" ref="log">
            <aop:around method="doarround" pointcut-ref="s1"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>

配置環繞通知後,只有通知方法執行,切入點原方法未執行。此時需要在通知方法中傳入ProceedingJoinPoint的實現類(定義該介面的引數,spring自動傳入實現類),再透過該實現類呼叫切入點方法

package com.rootpackage.mout;

import org.aspectj.lang.ProceedingJoinPoint;

public class log {
    public void doarround(ProceedingJoinPoint pjp){
        try {
            System.out.println ("列印日誌1");
            Object[] args=pjp.getArgs ();//獲取切入點引數
            pjp.proceed (args);//呼叫切入點方法
            System.out.println ("列印日誌2");
        } catch (Throwable throwable) {
            throwable.printStackTrace ( );
            System.out.println ("列印日誌3");
        } finally {
            System.out.println ("列印日誌4");
        }
    }
}

7註解

1)Aspect註解

表示該類是一個切面內

2)Before註解

表示該方法是一個前置通知

3)After註解

表示該方法是一個最終通知

4)AfterReturning註解

表示該方法是一個後置通知

5)AfterThrowing註解

表示該方法是一個異常通知

6)Arround註解

表示該方法是一個環繞通知

7)Point註解

定義一個切點,定義在一個方法上,引數是“execution(...)”,引用切面是需要呼叫該方法

在定義切面、通知前需要在配置檔案中配置

<?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 id="service1" class="com.rootpackage.services.service1"></bean>
    <bean id="log" class="com.rootpackage.mout.log"></bean>
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy><!--需要配置這一句-->
</beans>
package com.rootpackage.mout;

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


@Aspect//標識該類是一個切面類
public class log {

    @Pointcut("execution(* *..*.doservice(..))")//定義切點
    public void pf(){}

    @Before ("pf()")//定義前置通知
    public void dolog1(){
        System.out.println ("列印日誌1");
    }
    @AfterReturning("pf()")
    public void dolog2(){
        System.out.println ("列印日誌2");
    }
    @AfterThrowing("pf()")
    public void dolog3(){
        System.out.println ("列印日誌3");
    }
    @After ("pf()")
    public void dolog4(){
        System.out.println ("列印日誌4");
    }
}

執行後發現最終通知在後置通知之前執行,不合理。所以建議使用環繞通知

package com.rootpackage.mout;

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


@Aspect//標識該類是一個切面類
public class log {

    @Pointcut("execution(* *..*.doservice(..))")//定義切點
    public void pf(){}
    
	@Around ("pf()")
    public void doarround(ProceedingJoinPoint pjp){
        try {
            System.out.println ("列印日誌1");
            Object[] args=pjp.getArgs ();
            pjp.proceed (args);
            System.out.println ("列印日誌2");
        } catch (Throwable throwable) {
            throwable.printStackTrace ( );
            System.out.println ("列印日誌3");
        } finally {
            System.out.println ("列印日誌4");
        }
    }
}

9)不使用xml配置

此時應該在配置類上加一行註解@EnableAspectJAutoProxy

8轉賬案例使用切面新增事務

<?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/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="org.zhanghuan"></context:component-scan>
    <bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test2?serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=UTF-8"></property>
        <property name="user" value="root"></property>
        <property name="password" value=""></property>
    </bean>
    <bean class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg name="ds" ref="ds"></constructor-arg>
    </bean>
    <aop:config><!--將事務透過切面織入-->
        <aop:pointcut id="p1" expression="execution(* org.zhanghuan.service.AccountServiceImpl.*(..))"></aop:pointcut>
        <aop:aspect id="isp1" ref="transaction">
            <aop:before method="beginTransaction" pointcut-ref="p1"></aop:before>
            <aop:after method="release" pointcut-ref="p1"></aop:after>
            <aop:after-returning method="commit" pointcut-ref="p1"></aop:after-returning>
            <aop:after-throwing method="rollback" pointcut-ref="p1"></aop:after-throwing>
        </aop:aspect>
    </aop:config>
    </beans>

9DataSourceTransactionManager的使用

Spring的事務處理中,通用的事務處理流程是由抽象事務管理器AbstractPlatformTransactionManager來提供的,而具體的底層事務處理實現,由PlatformTransactionManager的具體實現類來實現

1)配置檔案方式

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

    <context:component-scan base-package="org.zhanghuan"></context:component-scan>
    <bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test2?serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=UTF-8"></property>
        <property name="user" value="root"></property>
        <property name="password" value=""></property>
    </bean>
    <bean class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg name="ds" ref="ds"></constructor-arg>
    </bean>

    <!--匯入事務管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!--這個包在spring-jdbc中,需在maven中匯入-->
        <property name="dataSource" ref="ds"></property>
    </bean>

    <!--配置事務通知-->
    <tx:advice id="manager" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED" read-only="false"></tx:method>
            <tx:method name="*find*" propagation="SUPPORTS" read-only="true"></tx:method><!--表示包含find關鍵字的方法,只讀,不一定包含事務-->
        </tx:attributes>
    </tx:advice>

    <!--配置aop,將事務通知織入-->
    <aop:config>
        <aop:pointcut id="p1" expression="execution(* org.zhanghuan.service.AccountServiceImpl.*(..))"></aop:pointcut>
        <aop:advisor advice-ref="manager" pointcut-ref="p1"></aop:advisor>
    </aop:config>
    </beans>

事務通知裡方法的屬性

屬性名 含義
isolation 事務隔離級別
propagation 指定事務傳播形式,預設REQUIRED,表示必有事務。對於查詢的方法,使用SUPPORTS
read-only 指定事務是否只讀,預設false,查詢方法設定未true
timeout 指定事務超時時間,預設未-1,表示永不超時。如果設定值,以秒未單位
rollback-for 指定一個異常,該異常發生,則回滾;其他異常發生,不回滾。無預設值,未設定表示出現任何異常都回滾
no-rollback-for 指定一個異常,該異常發生,不回滾;其他異常發生,回滾。無預設值,未設定表示出現任何異常都回滾

2)註解配置方式

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

    <context:component-scan base-package="org.zhanghuan"></context:component-scan>
    <bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test2?serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=UTF-8"></property>
        <property name="user" value="root"></property>
        <property name="password" value=""></property>
    </bean>
    <bean class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg name="ds" ref="ds"></constructor-arg>
    </bean>

    <!--匯入事務管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!--這個包在spring-jdbc中,需在maven中匯入-->
        <property name="dataSource" ref="ds"></property>
    </bean>

    <!--開啟spring對註解事務的支援-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

    <!--在需要開啟事務的地方表上註解@Transactional-->
    </beans>

在配置檔案中完成以上配置後,在需要開啟事務的地方表上註解@Transactional

3)無xml方式

//配置檔案
package org.zhanghuan.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@Configuration
@Import (jdbcConfig.class)
@ComponentScan("org.zhanghuan")
@EnableTransactionManagement//相當於xml中的tx:annotation-driven標籤+@Transactional註解
public class springConfig {
    @Bean//匯入事務管理器
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource ds){
        return new DataSourceTransactionManager(ds);
    }
}

找到事務實現類,並開啟事務


JDBCTemplate

1pom.xml匯入依賴

		<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>

2快速使用

//部分程式碼 		
 		//建立資料來源
       DriverManagerDataSource ds=new DriverManagerDataSource ();
       ds.setDriverClass ("com.mysql.jdbc.Driver");
       ds.setJdbcUrl ("jdbc:mysql://127.0.0.1:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8");
       ds.setUser ("root");
       ds.setPassword ("");

       JdbcTemplate template=new JdbcTemplate (ds);
       List<Map<String,Object>> list=template.queryForList ("select * from account");
       for (Map<String,Object> m:
            list) {
           System.out.println ("id:"+m.get ("id"));
       }

3query方法

query(String sql,Objectp[] args,RowMapper<T> rowmapper);//所有版本可用

query(String sql,RowMapper<T> rowmapper,Object.. args);//jdk1.5之後可用