把如下 jar 包加入到工程的 classpath 下:javascript
Spring 的配置文件: 一個典型的 Spring 項目須要建立一個或多個 Bean 配置文件, 這些配置文件用於在 Spring IOC 容器裏配置 Bean. Bean 的配置文件能夠放在 classpath 下, 也能夠放在其它目錄下.java
Helloworld.classmysql
public class Helloworld { private String name; public void setName(String name) { System.out.println("setName:"+name); this.name = name; } public void hello(){ System.out.println("hello:"+name); } }
輸出正則表達式
public static void main(String[] args) { // Helloworld helloworld = new Helloworld(); // helloworld.setName("tangsan"); // helloworld.hello(); //1.建立Spring的IOC容器對象 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //2.從IOC獲取 Bean 實例 Helloworld hello = (Helloworld)context.getBean("helloWorld"); hello.hello(); }
配置形式:spring
配置方式:sql
IOC 容器數據庫
依賴注入的方式:express
<!-- 配置 bean: class: bean 全類名,經過反射的方式在 IOC 容器中建立 Bean,因此要求 Bean 中必須有無參數的構造器 id : 惟一,bean名稱 --> <bean id="NameSetValue" class="com.cnblogs.tangge.spring.HelloWorld"> <property name="name" value="tangsansan"></property> </bean>
區別:apache
ApplicationContext 面向使用 Spring 框架的開發者,幾乎全部的應用場合都直接使用 ApplicationContext 而非底層的 BeanFactory。
ApplicationContext
的主要實現類:
ClassPathXmlApplicationContext
:從 類路徑下加載配置文件FileSystemXmlApplicationContext
: 從文件系統中加載配置文件ConfigurableApplicationContext
擴展於 ApplicationContext,新增長兩個主要方法:refresh() 和 close(), 讓 ApplicationContext 具備啓動、刷新和關閉上下文的能力調用 ApplicationContext 的 getBean() 方法
編程
<property>
元素, 使用 name 屬性指定 Bean 的屬性名稱,value 屬性或 <value>
子節點指定屬性值<bean id="NameSetValue" class="com.cnblogs.tangge.spring.HelloWorld"> <property name="name" value="tangsansan"></property> </bean>
<constructor-arg>
元素裏聲明屬性, <constructor-arg>
中沒有 name
屬性<bean id="carAndprice" class="Models.Car"> <constructor-arg value="Audi"></constructor-arg> <constructor-arg value="Shanghia"></constructor-arg> <constructor-arg value="300000"></constructor-arg> </bean> <!-- 構造方法注入配置 bean 的屬性 能夠指定參數的位置(index)和參數的類型(type) --> <bean id="carAndSpeed" class="Models.Car"> <constructor-arg value="BMW" type="java.lang.String"></constructor-arg> <constructor-arg value="Shanghia" index="1" type="java.lang.String"></constructor-arg> <constructor-arg value="240" type="int"></constructor-arg> </bean>
<![CDATA[]]>
把字面值包裹起來。<bean id="carAndSpeed" class="Models.Car"> <constructor-arg value="BMW" type="java.lang.String"></constructor-arg> <constructor-arg index="1" type="java.lang.String"> <value><![CDATA[<Shanghia>^]]></value> </constructor-arg> <constructor-arg value="240" type="int"></constructor-arg> </bean>
結果:
Car{brand='BMW', corp='<Shanghia>^', price=0.0, maxSpeed=240}
<ref>
元素或 ref 屬性爲 Bean 的屬性或構造器參數指定對 Bean 的引用.建立 Person 類
package Models; public class Person { private String name; private int age; private Car car; public Person() { } /** * 若是不建立3個構造函數,會報錯。Could not resolve matching constructor * @param name * @param age * @param car */ public Person(String name, int age, Car car) { this.name = name; this.age = age; this.car = car; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", car=" + car + '}'; } }
XML
注意 ref
<bean id="Person" class="Models.Person"> <constructor-arg value="10" type="int"></constructor-arg> <constructor-arg value="是男是女" type="java.lang.String"></constructor-arg> <constructor-arg name="car" ref="carAndSpeed"></constructor-arg> </bean>
執行
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Person person = (Person)context.getBean("Person"); System.out.println(person); // Person{name='是男是女', age=10, car=Car{brand='BMW', corp='<Shanghia>^', price=0.0, maxSpeed=240}}
<property>
或 <constructor-arg>
元素裏, 不須要設置任何 id 或 name 屬性<!--property 必須經過 setter 注入,因此必須在Person類添加 set方法--> <bean id="PersonIn" class="Models.Person"> <property name="name" value="內部Bean"></property> <property name="age" value="20"></property> <property name="car"> <bean id="carAndSpeed" class="Models.Car"> <constructor-arg value="benz" type="java.lang.String"></constructor-arg> <constructor-arg index="1" type="java.lang.String"> <value><![CDATA[<Shanghia>^]]></value> </constructor-arg> <constructor-arg value="300" type="int"></constructor-arg> </bean> </property> </bean>
null
空值和級聯屬性<null/>
元素標籤爲 Bean 的字符串或其它對象類型的屬性注入 null 值 ,或者<null></null>
null
空值
<constructor-arg name="car"><null/></constructor-arg>
級聯屬性
<bean id="PersonOut" class="Models.Person"> <constructor-arg value="10" type="int"></constructor-arg> <constructor-arg value="是男是女" type="java.lang.String"></constructor-arg> <constructor-arg name="car" ref="carAndSpeed"></constructor-arg> <!--Spring 支持級聯屬性的配置。property須要setter 注意:屬性須要先初始化才能夠爲級聯屬性賦值, 不然有異常,和 Structs2 不一樣--> <property name="car.maxSpeed" value="218"></property> </bean>
<list>
, <set>
或 <map>
) 來配置集合屬性.java.util.List
類型的屬性, 須要指定 <list>
標籤, 在標籤裏包含一些元素. 這些標籤能夠經過 <value>
指定簡單的常量值, 經過 <ref>
指定對其餘 Bean 的引用. 經過<bean>
指定內置 Bean 定義. 經過 <null/>
指定空元素. 甚至能夠內嵌其餘集合.<list>
<set>
標籤, 定義元素的方法與 List 同樣.<bean id="personlist" class="Models.PersonList"> <constructor-arg value="周星星"></constructor-arg> <constructor-arg value="50"></constructor-arg> <constructor-arg name="car"> <list> <ref bean="carAndSpeed" /> <ref bean="carAndprice" /> </list> </constructor-arg> </bean>
<map>
標籤訂義, <map>
標籤裏可使用多個 <entry>
做爲子標籤. 每一個條目包含一個鍵和一個值.<key>
標籤裏定義鍵<value>
, <ref>
, <bean>
或 <null>
元素.<entry>
的屬性定義: 簡單常量使用 key 和 value 來定義; Bean 引用經過 key-ref 和 value-ref 屬性定義<props>
定義 java.util.Properties, 該標籤使用多個 <prop>
做爲子標籤. 每一個 <prop>
標籤必須定義 key 屬性.<bean id="personmap" class="Models.PersonMap"> <constructor-arg value="周星星"></constructor-arg> <constructor-arg value="50"></constructor-arg> <constructor-arg name="car"> <!--使用map節點及map的entry子節點配置 Map類型的成員變量--> <map> <entry key="一號車" value-ref="carAndSpeed"></entry> <entry key="二號車" value-ref="carAndprice"></entry> </map> </constructor-arg> </bean>
PersonMap person = (PersonMap)context.getBean("personmap"); System.out.println(person.toString()); //Person{name='周星星', age=50, car={一號車=Car{brand='BMW', corp='<Shanghia>^--', price=0.0, maxSpeed=218}, // 二號車=Car{brand='Audi', corp='Shanghia', price=0.0, maxSpeed=300000}}}
建立一個 DataSource 類
public class DataSource { private Properties properties; public Properties getProperties() { return properties; } public void setProperties(Properties properties) { this.properties = properties; } }
配置Bean
<bean id="datasource" class="Models.DataSource"> <property name="properties"> <!--使用 props 和 prop 子節點爲 properties 賦值--> <props> <prop key="user">root</prop> <prop key="pass">123</prop> <prop key="jdbcurl">jdbc:mysql:///test</prop> <prop key="driverClass">com.mysql.jdbc.Driver</prop> </props> </property> </bean>
調用
DataSource source = context.getBean(DataSource.class); System.out.println(source.getProperties()); //{jdbcurl=jdbc:mysql:///test, driverClass=com.mysql.jdbc.Driver, user=root, pass=123}
可使用 util schema 裏的集合標籤訂義獨立的集合 Bean. 須要注意的是, 必須在 根元素裏添加 util schema 定義
<!--導入 util 命名空間 http://www.springframework.org/schema/util--> <util:list id="utilcars"> <ref bean="carAndSpeed" /> <ref bean="carAndprice" /> </util:list> <util:map id="utilmap"> <entry key="一號車" value-ref="carAndSpeed"></entry> <entry key="二號車" value-ref="carAndprice"></entry> </util:map> <bean id="personmap" class="Models.PersonMap"> <constructor-arg value="周星星"></constructor-arg> <constructor-arg value="50"></constructor-arg> <constructor-arg name="car" ref="utilmap"> <!--使用map節點及map的entry子節點配置 Map類型的成員變量--> <!--<map>--> <!--<entry key="一號車" value-ref="carAndSpeed"></entry>--> <!--<entry key="二號車" value-ref="carAndprice"></entry>--> <!--</map>--> </constructor-arg> </bean>
爲了簡化 XML 文件的配置,愈來愈多的 XML 文件採用屬性而非子元素配置信息。
Spring 從 2.5 版本開始引入了一個新的 p
命名空間,能夠經過 <bean>
元素屬性的方式配置 Bean 的屬性。
使用 p 命名空間後,基於 XML 的配置方式將進一步簡化
<bean id="PersonWithP" class="Models.Person" p:name="孔明" p:age="33"></bean>
<bean>
的 autowire
屬性裏指定自動裝配的模式缺點:
通常狀況下,在實際的項目中不多使用自動裝配功能,由於和自動裝配功能所帶來的好處比起來,明確清晰的配置文檔更有說服力一些
<bean id="address" class="com.cnblogs.tangge.autowire.Address" p:city="重慶" p:street="小龍坎"></bean> <bean id="car" class="com.cnblogs.tangge.autowire.Car" p:brand="Audi" p:price="300000"></bean> <!-- 使用autowire屬性指定自動裝配方式: byName:根據 bean 的名字和當前 bean 的setter 風格的屬性名進行自動裝配。 byType:根據 bean 的屬性類型自動裝配。 --> <bean id="person" class="com.cnblogs.tangge.autowire.Person" p:name="Tom" autowire="byName"></bean> </beans>
1).繼承
abstract
屬性爲
true
, 這樣 Spring 將不會實例化這個 Bean
<bean>
元素裏的全部屬性都會被繼承. 好比: autowire, abstract 等.parent
繼承
<bean id="address" class="com.cnblogs.tangge.autowire.Address" p:city="重慶" p:street="小龍坎"></bean> <!--bean 配置的繼承:使用 parent 屬性指定繼承哪一個 bean 的配置,這裏繼承了 class與 p:street--> <bean id="address2" p:city="重慶1" parent="address"></bean>
結果
Address address =(Address) context.getBean("address"); System.out.println(address); address =(Address) context.getBean("address2"); System.out.println(address); /* Address{city='重慶', street='小龍坎'} Address{city='重慶1', street='小龍坎'} */
abstract
抽象,做爲模板
<!-- 1.若只想把父 Bean 做爲模板, 能夠設置 <bean> 的abstract 屬性爲 true, 這樣 Spring 將不會實例化這個 Bean,只能繼承 2.若是沒有 class 屬性,則必須是一個抽象 bean --> <bean id="address" class="com.cnblogs.tangge.autowire.Address" p:city="重慶" p:street="小龍坎" abstract="true"></bean> <!--bean 配置的繼承:使用 parent 屬性指定繼承哪一個 bean 的配置,這裏繼承了 class與 p:street--> <bean id="address2" p:city="重慶1" parent="address"></bean>
2).依賴
做用:depends-on用來指定Bean初始化及銷燬時的順序。
depends-on
屬性設定 Bean 前置依賴的Bean,前置依賴的 Bean 會在本 Bean 實例化以前建立好<bean id="car" class="com.cnblogs.tangge.autowire.Car" p:brand="Audi" p:price="300000"></bean> <!--要求再配置person時,必須有一個關聯car. id="personDependsOn" 這個bean依賴於 id="car" 這個bean--> <bean id="personDependsOn" class="com.cnblogs.tangge.autowire.Person" p:name="www" p:address-ref="address2" depends-on="car"></bean>
<bean>
元素的 scope 屬性裏設置 Bean 的做用域.類別 | 說明 |
---|---|
singleton | 在 SpringIOC 容器中只存在一個實例,Bean以單實例的形式存在 |
prototype | 每次調用 getBean() 返回一個新實例 |
request | 每次HTTP請求,返回一個新Bean,該做用域適用 WebApplicationContext環境 |
session | 同一個HTTP Session共享一個Bean,不一樣HTTP Session使用不一樣的Bean。該做用域適用 WebApplicationContext環境 |
<bean id="car" class="com.cnblogs.tangge.autowire.Car" p:brand="Audi" p:price="300000"></bean>
代碼
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-scope.xml"); Car car1= (Car) ctx.getBean("car"); Car car2= (Car) ctx.getBean("car"); System.out.println(car1.equals(car2)); //true
添加 scope="prototype"
<bean id="car" class="com.cnblogs.tangge.autowire.Car" p:brand="Audi" p:price="300000" scope="prototype"></bean>
返回結果爲: System.out.println(car1.equals(car2)); //false
${var}
的變量, PropertyPlaceholderConfigurer 從屬性文件里加載屬性, 並使用這些屬性來替換變量.${propName}
,以實現屬性之間的相互引用。示例:
導入mysql和jdbc的jar包
<bean id="datasource" class="org.apache.commons.dbcp2.BasicDataSource" > <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/day22_jdbc?serverTimezone = GMT"></property> <property name="username" value="root"></property> <property name="password" value="123"></property> </bean>
java
DataSource dataSource = (DataSource) ctx.getBean("datasource"); System.out.println(dataSource.getConnection()); //1763344271, URL=jdbc:mysql://localhost:3306/day22_jdbc?serverTimezone = GMT, UserName=root@localhost, MySQL Connector/J
db.properties
drivername=com.mysql.cj.jdbc.Driver url = jdbc:mysql://localhost:3306/day22_jdbc?serverTimezone = GMT user = root pass = 123
bean設置
<context:property-placeholder location="classpath:db.properties"/> <!--使用外部配置文件--> <bean id="datasource2" class="org.apache.commons.dbcp2.BasicDataSource" > <property name="driverClassName" value="${drivername}"></property> <property name="url" value="${url}"></property> <property name="username" value="${user}"></property> <property name="password" value="${pass}"></property> </bean>
address.class
package com.cnblogs.tangge.spel; public class Address { private String city; private String street; public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } @Override public String toString() { return "Address{" + "city='" + city + '\'' + ", street='" + street + '\'' + '}'; } }
car.class
package com.cnblogs.tangge.spel; public class Car { public Car() { System.out.println("default constuct..."); } //品牌 private String brand; private double price; //輪胎周長 private double tyrePerimeter; public void setBrand(String brand) { this.brand = brand; } //#{car.price > 300000 ?'金領':'白領'}的時候,price 這裏必須有getter方法。 public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } @Override public String toString() { return "Car{" + "brand='" + brand + '\'' + ", price=" + price + ", tyrePerimeter=" + tyrePerimeter + '}'; } public void setTyrePerimeter(double tyrePerimeter) { this.tyrePerimeter = tyrePerimeter; } }
person.class
package com.cnblogs.tangge.spel; /** * @Description: * @Package: com.cnblogs.tangge.autowire * @ClassName: Person * @Author: tangge * @CreateDate: 2018年08月14 16:39 * @Version: 1.0 **/ public class Person { private String name; private Car car; //引用 address bean 的 city 屬性 private String city; //根據car的 price 決定 info:car 的 price >=300000 private String info; @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", car=" + car + ", city='" + city + '\'' + ", info='" + info + '\'' + '}'; } public void setName(String name) { this.name = name; } public void setCar(Car car) { this.car = car; } public void setCity(String city) { this.city = city; } public void setInfo(String info) { this.info = info; } }
<?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="address" class="com.cnblogs.tangge.spel.Address"> <!--spel 字面量--> <property name="city" value="#{'Beijing'}"></property> <property name="street" value="Wudaokou"></property> </bean> <bean id="car" class="com.cnblogs.tangge.spel.Car"> <!--spel 字面量--> <property name="brand" value="#{'audi'}"></property> <property name="price" value="#{300000}"></property> <!--spel 調用類的靜態屬性 T()--> <property name="tyrePerimeter" value="#{T(java.lang.Math).PI * 80}"></property> </bean> <bean id="person" class="com.cnblogs.tangge.spel.Person"> <property name="name" value="#{'tangsansan'}"></property> <!--spel 引用其餘對象的屬性--> <property name="city" value="#{address.city}"></property> <!--spel 引用其餘對象--> <property name="car" value="#{car}"></property> <!--spel if esle變體--> <property name="info" value="#{car.price > 300000 ?'金領':'白領'}"></property> </bean> </beans> <!-- Address{city='Beijing', street='Wudaokou'} Car{brand='audi', price=300000.0, tyrePerimeter=251.32741228718345} Person{name='tangsansan', car=Car{brand='audi', price=300000.0, tyrePerimeter=251.32741228718345}, city='Beijing', info='白領'} -->
package com.cnblogs.tangge.cycle; public class Car { private String brand; public Car() { System.out.println("Car constructor..."); } public void setBrand(String brand) { System.out.println("setBrand.."); this.brand = brand; } public void init(){ System.out.println("init...."); } public void destroy(){ System.out.println("destroy...."); } @Override public String toString() { return "Car{" + "brand='" + brand + '\'' + '}'; } }
Bean配置
<bean id="car" class="com.cnblogs.tangge.cycle.Car" init-method="init" destroy-method="destroy"> <property name="brand" value="audi"></property> </bean>
測試
public static void main(String[] args) { //ApplicationContext 擴展類,支持close() ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("bean-cycle.xml"); Car car = (Car) ctx.getBean("car"); System.out.println(car); ctx.close(); /* Car constructor... //構造器 setBrand.. //setter init.... //init() Car{brand='audi'} //toString() destroy.... //destroy() */ }
BeanPostProcessor
BeanPostProcessor
接口. 在初始化方法被調用先後, Spring 將把每一個 Bean 實例分別傳遞給上述接口的如下兩個方法:
新建一個MyBeanPostprocessor.class
package com.cnblogs.tangge.cycle; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class MyBeanPostprocessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.printf("postProcessBeforeInitialization:%s,%s %n",bean,beanName); if ("car".equals(beanName)){ Car car = new Car(); car.setBrand("BMW"); return car; } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.printf("postProcessAfterInitialization:%s,%s %n",bean,beanName); return bean; } }
配置bean
<!-- 實現BeanPostProcessor接口,提供具體接口 Object postProcessBeforeInitialization(Object bean, String beanName) init-method 以前被調用 Object postProcessAfterInitialization(Object bean, String beanName) init-method 以後被調用 bean:bean 實例自己 beanName: IOC 容器配置的 bean 的名字 返回值:是實際上返回給用戶的那個 bean,注意:以上兩個方法修改 bean,甚至返回一個新的 bean --> <!--配置 bean 後置處理器:不須要id,IOC容器自動識別是一個 BeanPostProcessor--> <bean class="com.cnblogs.tangge.cycle.MyBeanPostprocessor"></bean>
運行
public static void main(String[] args) { //ApplicationContext 擴展類,支持close() ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("bean-cycle.xml"); Car car = (Car) ctx.getBean("car"); System.out.println(car); ctx.close(); /** Car constructor... //1.構造器 setBrand.. //2.setter postProcessBeforeInitialization:Car{brand='audi'},car //3.postProcessBeforeInitialization Car constructor... //postProcessBeforeInitialization 先new個car 在stter setBrand.. init.... //4.init postProcessAfterInitialization:Car{brand='BMW'},car //5.postProcessAfterInitialization Car{brand='BMW'} //toString() destroy.... //6.destroy() */ }
調用靜態工廠方法建立 Bean是將對象建立的過程封裝到靜態方法
中. 當客戶端須要對象時, 只須要簡單地調用靜態方法, 而不一樣關心建立對象的細節.
要聲明經過靜態方法建立的 Bean, 須要在 Bean 的 class 屬性裏指定擁有該工廠的方法的類, 同時在 factory-method
屬性裏指定工廠方法的名稱. 最後, 使用 <constrctor-arg>
元素爲該方法傳遞方法參數.
package com.cnblogs.tangge.factory; import java.util.HashMap; import java.util.Map; public class staticCarFactory { private static Map<String,Car> cars = new HashMap<>(); static { cars.put("audi",new Car("audi",300000)); cars.put("ford",new Car("ford",230000)); } //靜態方法 public static Car getCar(String name){ return cars.get(name) ; } }
Bean
constructor-arg:若是工廠方法須要傳入參數,則使用constructor-arg來配置參數
<!--靜態方法配置bean,不是配置工廠方法實例,而是bean實例--> <!-- class:指向靜態方法全類名 factory-method:指向靜態工廠方法的名字 constructor-arg:若是工廠方法須要傳入參數,則使用constructor-arg來配置參數 --> <bean id="car" class="com.cnblogs.tangge.factory.staticCarFactory" factory-method="getCar"> <constructor-arg value="ford"></constructor-arg> </bean>
這裏測試的結果爲:Car{brand='ford', price=230000.0}
要聲明經過實例工廠方法建立的 Bean
factory-bean
屬性裏指定擁有該工廠方法的 Beanfactory-method
屬性裏指定該工廠方法的名稱package com.cnblogs.tangge.factory; import java.util.HashMap; import java.util.Map; /** * @Description:實例工廠的方法 * **/ public class InstanceCarFactory { private static Map<String,Car> cars = null; public InstanceCarFactory() { cars = new HashMap<>(); cars.put("audi",new Car("audi",300000)); cars.put("ford",new Car("ford",230000)); } public Car getInstanceCar(String name){ return cars.get(name) ; } }
Bean
<!--實例工廠方法--> <bean id="carFactory" class="com.cnblogs.tangge.factory.InstanceCarFactory"/> <bean id="car2" factory-bean="carFactory" factory-method="getInstanceCar"> <constructor-arg value="audi"></constructor-arg> </bean>
調用結果:Car{brand='audi', price=300000.0}
package org.springframework.beans.factory; public interface FactoryBean<T> { T getObject() throws Exception; Class<?> getObjectType(); boolean isSingleton(); }
FactoryBean 一般是用來建立比較複雜的bean,通常的bean 直接用xml配置便可,但若是一個bean的建立過程當中涉及到不少其餘的bean 和複雜的邏輯,用xml配置比較困難,這時能夠考慮用FactoryBean。
建立個人FactoryBean
package com.cnblogs.tangge.factoryBean; import org.springframework.beans.factory.FactoryBean; public class MyFactoryBean implements FactoryBean<Car> { private String brand; public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } /** * * @return 返回bean的對象 * @throws Exception */ @Override public Car getObject() throws Exception { return new Car(brand,500000); } /** * * @return bean的類型 */ @Override public Class<?> getObjectType() { return Car.class; } /** * * @return 是否爲單例 */ @Override public boolean isSingleton() { return false; } }
Bean
<!-- 經過FactoryBean 配置 Bean實例 class: 指向 FactoryBean 的全類名 property: 配置 FactoryBean 屬性 但實際返回的實例倒是 FactoryBean 的 getObject() 方法返回的實例 --> <bean id="car" class="com.cnblogs.tangge.factoryBean.MyFactoryBean"> <property name="brand" value="BMW"></property> </bean>
測試結果:Car{brand='BMW', price=500000.0}
@Component
: 基本註解, 標識了一個受 Spring 管理的組件@Respository
: 標識持久層組件@Service
: 標識服務層(業務層)組件@Controller
: 標識表現層組件<context:component-scan>
:
base-package
屬性指定一個須要掃描的基類包,Spring 容器將會掃描這個基類包裏及其子包中的全部類.resource-pattern
屬性過濾特定的類,示例:<context:include-filter>
子節點表示要包含的目標類<context:exclude-filter>
子節點表示要排除在外的目標類context:include-filter 和 context:exclude-filter 子節點支持多種類型的過濾表達式:
Filter Type | 示例 | 描述 |
---|---|---|
annotation | org.example.SomeAnnotation | 符合SomeAnnoation的target class |
assignable | org.example.SomeClass | 指定class或interface的全名 |
aspectj | org.example..*Service+ | AspectJ語法 |
regex | org.example.Default.* | Regelar Expression |
custom | org.example.MyTypeFilter | Spring3新增自訂Type,實做org.springframework.core.type.TypeFilter |
TestObject.class
package com.cnblogs.tangge.annoncation; import org.springframework.stereotype.Component; @Component public class TestObject { }
UserController.class
package com.cnblogs.tangge.annoncation.Controller; import org.springframework.stereotype.Controller; @Controller public class UserController { public void execute(){ System.out.println("UserController execute..."); } }
UserService.class
package com.cnblogs.tangge.annoncation.Service; import org.springframework.stereotype.Service; @Service public class UserService { public void add(){ System.out.println("UserService add..."); } }
接口 UserRepository.class
package com.cnblogs.tangge.annoncation.Repository; public interface UserRepository { void save(); }
UserRepositoryImp.class
package com.cnblogs.tangge.annoncation.Repository; import org.springframework.stereotype.Repository; @Repository("userRepository") public class UserRepositoryImp implements UserRepository { @Override public void save() { System.out.println("UserRepository save.."); } }
bean-annocation.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"> <!--指定Spring掃描的包--> <context:component-scan base-package="com.cnblogs.tangge.annoncation"></context:component-scan> </beans>
測試
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-annocation.xml"); TestObject test = (TestObject) ctx.getBean("testObject"); System.out.println(test); UserController controller = (UserController) ctx.getBean("userController"); System.out.println(controller); UserService service = (UserService) ctx.getBean("userService"); System.out.println(service); UserRepository repository = (UserRepository) ctx.getBean("userRepository"); System.out.println(repository); } /* com.cnblogs.tangge.annoncation.TestObject@1990a65e com.cnblogs.tangge.annoncation.Controller.UserController@64485a47 com.cnblogs.tangge.annoncation.Service.UserService@25bbf683 com.cnblogs.tangge.annoncation.Repository.UserRepositoryImp@6ec8211c */
若是僅但願掃描特定的類而非基包下的全部類,可以使用 resource-pattern
屬性過濾特定的類
<context:component-scan base-package="com.cnblogs.tangge.annoncation" resource-pattern="repository/*.class"></context:component-scan>
報錯:No bean named 'testObject' available
只能掃描 UserRepository
// TestObject test = (TestObject) ctx.getBean("testObject"); // System.out.println(test); // UserController controller = (UserController) ctx.getBean("userController"); // System.out.println(controller); // UserService service = (UserService) ctx.getBean("userService"); // System.out.println(service); UserRepository repository = (UserRepository) ctx.getBean("userRepository"); System.out.println(repository);
<context:component-scan base-package="com.cnblogs.tangge.annoncation"> <!--不包含Repository類--> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository" /> </context:component-scan>
執行
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-annocation.xml"); TestObject test = (TestObject) ctx.getBean("testObject"); System.out.println(test); UserController controller = (UserController) ctx.getBean("userController"); System.out.println(controller); UserService service = (UserService) ctx.getBean("userService"); System.out.println(service); UserRepository repository = (UserRepository) ctx.getBean("userRepository"); System.out.println(repository); } /* com.cnblogs.tangge.annoncation.TestObject@1990a65e com.cnblogs.tangge.annoncation.Controller.UserController@64485a47 com.cnblogs.tangge.annoncation.Service.UserService@25bbf683 Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userRepository' available 'userRepository' 不可用 */
<!-- context:include-filter:指定包含哪些表達式的組件,必須與use-default-filters配合使用 use-default-filters:設置爲false --> <context:component-scan base-package="com.cnblogs.tangge.annoncation" use-default-filters="false"> <!--只包含Repository類--> <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" /> </context:component-scan>
這裏執行,直接失敗,由於只能實例 userRepository,其餘都失敗。
指定class或interface的全名
<context:component-scan base-package="com.cnblogs.tangge.annoncation" use-default-filters="false"> <!--只包含Repository類, type=assignable : 指定class或interface的全名 expression : class全名 --> <context:include-filter type="assignable" expression="com.cnblogs.tangge.annoncation.Repository.UserRepository" /> </context:component-scan>
和上面效果同樣,只能實例化 userRepository。
<context:component-scan>
元素還會自動註冊 AutowiredAnnotationBeanPostProcessor 實例, 該實例能夠自動裝配具備 @Autowired
和 @Resource
、@Inject
註解的屬性.
@Autowired
<context:component-scan base-package="com.cnblogs.tangge.annoncation"> </context:component-scan>
UserController.class
調用 userService.add();
package com.cnblogs.tangge.annoncation.Controller; import com.cnblogs.tangge.annoncation.Service.UserService; import org.springframework.stereotype.Controller; @Controller public class UserController { private UserService userService; public void execute(){ System.out.println("UserController execute..."); userService.add(); } }
UserService.class
調用 userRepository.save();
package com.cnblogs.tangge.annoncation.Service; import com.cnblogs.tangge.annoncation.Repository.UserRepository; import org.springframework.stereotype.Service; @Service public class UserService { private UserRepository userRepository; public void add(){ System.out.println("UserService add..."); userRepository.save(); } }
調用
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-annocation.xml"); UserController controller = (UserController) ctx.getBean("userController"); controller.execute(); }
執行結果:
UserController execute... at com.cnblogs.tangge.annoncation.Controller.UserController.execute(UserController.java:13) at com.cnblogs.tangge.annoncation.annocationMain.main(annocationMain.java:29)
咱們UserController在添加@Autowired
@Controller public class UserController { @Autowired private UserService userService; public void execute(){ System.out.println("UserController execute..."); userService.add(); } }
也在UserService添加@Autowired
@Service public class UserService { @Autowired private UserRepository userRepository; public void add(){ System.out.println("UserService add..."); userRepository.save(); } }
執行結果:
UserController execute...
UserService add...
UserRepository save..
package com.cnblogs.tangge.annoncation.Repository; import com.cnblogs.tangge.annoncation.TestObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; @Repository("userRepository") public class UserRepositoryImp implements UserRepository { @Autowired TestObject testObject; @Override public void save() { System.out.println("UserRepository save.."); System.out.println(testObject); } }
而後把TestObject的@Component 去掉
package com.cnblogs.tangge.annoncation; public class TestObject { }
報錯:
DependencyException: Error creating bean with name 'userRepository': Unsatisfied dependency expressed through field 'testObject';
修改 @Autowired(required = false) 不檢查 TestObject
@Repository("userRepository") public class UserRepositoryImp implements UserRepository { @Autowired(required = false) TestObject testObject; @Override public void save() { System.out.println("UserRepository save.."); System.out.println(testObject); } }
執行結果:
UserController execute...
UserService add...
UserRepository save..
null
@Qualifiter
已指定注入 Bean 的名稱UserRepositoryImp修改,去掉命名@Repository,使用默認
/@Repository("userRepository") @Repository public class UserRepositoryImp implements UserRepository { @Autowired(required = false) TestObject testObject; @Override public void save() { System.out.println("UserRepository save.."); System.out.println(testObject); } }
建立第2個Repository,UserjdbcRrpository.class
package com.cnblogs.tangge.annoncation.Repository; import org.springframework.stereotype.Repository; @Repository public class UserjdbcRrpository implements UserRepository { @Override public void save() { System.out.println("UserjdbcRrpository save..."); } }
執行結果:
報錯:expected single matching bean but found 2: userjdbcRrpository,userRepositoryImp
下面,修改UserService,添加 @Qualifier,指定注入的Bean名稱
@Service public class UserService { @Autowired @Qualifier("userjdbcRrpository") private UserRepository userRepository; public void add(){ System.out.println("UserService add..."); userRepository.save(); } }
執行結果:
UserController execute...
UserService add...
UserjdbcRrpository save...
實際上,@Qualifier("userjdbcRrpository")
還能夠寫到形參前面,
@Service public class UserService { private UserRepository userRepository; @Autowired public void setUserRepository(@Qualifier("userjdbcRrpository") UserRepository userRepository) { this.userRepository = userRepository; } public void add(){ System.out.println("UserService add..."); userRepository.save(); } }
Spring 4.x 中能夠爲子類注入子類對應的泛型類型的成員變量的引用
AspectJ:Java 社區裏最完整最流行的 AOP 框架.
在 Spring2.0 以上版本中, 可使用基於 AspectJ 註解或基於 XML 配置的 AOP
一、aspectjweaver.jar 下載地址:aspectjweaver.jar
二、下載
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aop-4.3.18.RELEASE.jar
spring-aspects-4.3.18.RELEASE.jar
<beans>
根元素中.<aop:aspectj-autoproxy>
<aop:aspectj-autoproxy>
元素時, 會自動爲與 AspectJ 切面匹配的 Bean 建立代理.用 AspectJ 註解聲明切面
<aop:aspectj-autoproxy/>
bean
<context:component-scan base-package="com.cnblogs.tangge"></context:component-scan> <!--使用 AspjectJ 註解起做用:自動爲匹配的類生成代理對象--> <aop:aspectj-autoproxy/>
接口ArithmeticCalculator
package com.cnblogs.tangge.spring.aop.impl; public interface ArithmeticCalculator { int add(int i, int j); int sub(int i, int j); int mul(int i, int j); int div(int i, int j); }
實現類ArithmeticCalculatorImpl
package com.cnblogs.tangge.spring.aop.impl; import org.springframework.stereotype.Component; @Component public class ArithmeticCalculatorImpl implements ArithmeticCalculator { @Override public int add(int i, int j) { int result = i+j; return result; } @Override public int sub(int i, int j) { int result = i-j; return result; } @Override public int mul(int i, int j) { int result = i*j; return result; } @Override public int div(int i, int j) { int result = i/j; return result; } }
建立一個AOP
package com.cnblogs.tangge.spring.aop.impl; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; //把這個類聲明爲一個切面:須要把該類放入IOC容器中。再聲明爲一個切面 @Aspect @Component public class LogAspect { //聲明一個前置通知:在目標方法開始以前 @Before("execution(public int com.cnblogs.tangge.spring.aop.impl.ArithmeticCalculator.add(int,int))") public void beforeMethod() { System.out.println("the method add begins"); } public void afterMethod() { System.out.println("sub result:"); } }
測試
public class demo { public static void main(String[] args) { //1.建立Spring 的IOC容器 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //2.從IOC 容器獲取 bean ArithmeticCalculator arithmeticCalculator = ctx.getBean(ArithmeticCalculator.class); //3.使用bean int result = arithmeticCalculator.add(1,3); System.out.println("result:"+result); /* the mothod add begins result:4 */ } }
package com.cnblogs.tangge.spring.aop.impl; import java.util.Arrays; import java.util.List; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; //把這個類聲明爲一個切面:須要把該類放入IOC容器中。再聲明爲一個切面 @Aspect @Component public class LogAspect { //聲明一個前置通知:在目標方法開始以前 @Before("execution(public int com.cnblogs.tangge.spring.aop.impl.ArithmeticCalculator.*(int,int))") public void beforeMethod(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); //Arrays.asList(T... a) 返回由指定數組支持的固定大小的列表。 List<Object> args = Arrays.asList(joinPoint.getArgs()); System.out.println("前置通知@Before:the method "+methodName+" begins"+args); } /** * 方法執行後,執行的代碼 * @param joinPoint */ @After("execution(public int com.cnblogs.tangge.spring.aop.impl.ArithmeticCalculator.*(..))") public void afterMethod(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println("後置通知@After:the method "+methodName+" end.."); } /** * 返回通知, 在方法返回結果以後執行 * @param joinPoint * @param result1 能夠訪問方法返回值 */ @AfterReturning(value = "execution(public int com.cnblogs.tangge.spring.aop.impl.ArithmeticCalculator.*(..))",returning = "result1") public void afterReturningMethod(JoinPoint joinPoint,Object result1) { String methodName = joinPoint.getSignature().getName(); System.out.println("返回通知@AfterReturning:the method "+methodName+" AfterReturning..The result with:"+result1); } /** * 異常通知,能夠訪問異常對象,且能夠指定出現特定異常時在執行通知代碼 * @param joinPoint * @param exception */ @AfterThrowing(value = "execution(public int com.cnblogs.tangge.spring.aop.impl.ArithmeticCalculator.*(..))", throwing = "exception") public void afterThrowingMethod(JoinPoint joinPoint,Exception exception) { String methodName = joinPoint.getSignature().getName(); System.out.println("異常通知@AfterReturning:the method "+methodName+" throwing exception:"+exception); } }
執行結果:
前置通知@Before:the method add begins[1, 3]
後置通知@After:the method add end..
返回通知@AfterReturning:the method add AfterReturning..The result with:4
result:4
前置通知@Before:the method div begins[10, 2]
後置通知@After:the method div end..
返回通知@AfterReturning:the method div AfterReturning..The result with:5
result:5
/** * 環繞通知 * @param joinPoint */ @Around(value = "execution(public int com.cnblogs.tangge.spring.aop.impl.ArithmeticCalculator.*(..))") public Object AroundMethod(ProceedingJoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); System.out.println("-----進入環繞通知-----"); //執行目標方法 Object result = null; try { System.out.println("前置通知@Before:the method "+methodName+" begins"+ Arrays.asList(joinPoint.getArgs())); //執行目標方法 result = joinPoint.proceed(); System.out.println("返回通知@AfterReturning:the method "+methodName+" AfterReturning..The result with:"+result); } catch (Throwable throwable) { System.out.println("異常通知@AfterReturning:the method "+methodName+" throwing exception:"+throwable.toString()); throwable.printStackTrace(); } System.out.println("後置通知@After:the method "+methodName+" end.."); return result; }
執行結果:
-----進入環繞通知-----
前置通知@Before:the method add begins[1, 3]
返回通知@AfterReturning:the method add AfterReturning..The result with:4
後置通知@After:the method add end..
result:4
-----進入環繞通知-----
前置通知@Before:the method div begins[10, 2]
返回通知@AfterReturning:the method div AfterReturning..The result with:5
後置通知@After:the method div end..
result:5
切面的優先級能夠經過實現 Ordered 接口或利用 @Order
註解指定.
實現 Ordered 接口, getOrder()
方法的返回值越小, 優先級越高.
若使用 @Order 註解, 序號出如今註解中
/** * @Pointcut 聲明切入點表達式 * 其餘通知直接使用方法名引用當前的切入點 */ @Pointcut("execution(public int com.cnblogs.tangge.spring.aop.impl.ArithmeticCalculator.*(..))") public void declarexecution() {}
其餘方法經過 @Pointcut
註解將一個切入點聲明成簡單的方法. 切入點的方法體一般是空的, 由於將切入點定義與應用程序邏輯混在一塊兒是不合理的.
/** * 環繞通知 * @param joinPoint */ @Around(value = "declarexecution()") public Object AroundMethod(ProceedingJoinPoint joinPoint) { ... }
<?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" 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"> <!--1.配置bean--> <bean id="arithmeticCalculator" class="com.cnblogs.tangge.spring.aop.impl.ArithmeticCalculatorImpl"></bean> <!--2.配置切面的bean--> <bean id="logAspect" class="com.cnblogs.tangge.spring.aop.impl.LogAspect"></bean> <!--3.配置AOP--> <aop:config> <!--配置切點表達式--> <aop:pointcut id="pointcut" expression="execution(public int com.cnblogs.tangge.spring.aop.impl.ArithmeticCalculator.*(..))"></aop:pointcut> <!--配置切面及通知--> <aop:aspect ref="logAspect" order="2"> <aop:around method="AroundMethod" pointcut-ref="pointcut"></aop:around> </aop:aspect> </aop:config> </beans>
db.properties
drivername=com.mysql.cj.jdbc.Driver url = jdbc:mysql://localhost:4040/day22_jdbc?serverTimezone = GMT user = root pass = 123
Bean配置 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" 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"> <!--引用外部文件db.properties--> <context:property-placeholder location="classpath:db.properties"/> <!--配置jdbc--> <bean id="datasource" class="org.apache.commons.dbcp2.BasicDataSource" > <property name="driverClassName" value="${drivername}"></property> <property name="url" value="${url}"></property> <property name="username" value="${user}"></property> <property name="password" value="${pass}"></property> </bean> <!--配置Spring的JdbcTemplate--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="datasource"></property> </bean> <!--指定Spring掃描的包 <context:component-scan> 元素還會自動註冊 AutowiredAnnotationBeanPostProcessor 實例, 該實例能夠自動裝配具備 @Autowired 和 @Resource 、@Inject註解的屬性. --> <context:component-scan base-package="com.cnblogs.tangge.jdbc"> </context:component-scan> </beans>
下面進行測試
package com.cnblogs.tangge.jdbc; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import javax.sql.DataSource; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; public class JDBCTest { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); JdbcTemplate jdbcTemplate = (JdbcTemplate) ctx.getBean("jdbcTemplate"); public static void main(String[] args) { JDBCTest test = new JDBCTest(); try { test.UpdateTemplate(); } catch (Exception e) { e.printStackTrace(); } } /** * 更新:insert,update,delete */ public void UpdateTemplate() { String sql = "UPDATE `day22_jdbc`.`sort` SET `sname`=? WHERE (`sid`=?);\n "; jdbcTemplate.update(sql, "什麼球", 5); } /** * 批量更新:insert,update,delete */ public void InsertTemplate() { String sql = "insert into sort(`sname`, `sprice`, `sdesc`) VALUES (?,?,?) "; List<Object[]> batchList = new ArrayList<>(); batchList.add(new Object[]{"棒球", 36.22, "體育用品"}); batchList.add(new Object[]{"冰箱", 1466.99, "加點用品"}); jdbcTemplate.batchUpdate(sql, batchList); } /** * 讀取一條數據,獲得對應的對象 * 注意不是調用 queryForObject(String sql, Class<T> requiredType, Object... args) * 須要: queryForObject(String sql, RowMapper<T> rowMapper, Object... args) * 1.RowMapper<T> 指定如何去映射行結果集的行,經常使用實現類 BeanPropertyRowMapper * 2.不支持級聯屬性 JdbcTemplate 是一個小工具,不是 ORM 框架 */ public void testQueryForObject() { String sql = "select * from employee where id = ?"; //Employee employee =jdbcTemplate.queryForObject(sql,Employee.class,1); RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class); Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, 1); System.out.println(employee); //結果:Employee{id=1, lastName='sansan', firstName='tang', email='fenm@qq.com'} } /** * 讀取一條數據,獲得對應的對象 * 配置Dao,調用Dao封裝方法 */ public void testQueryForObjectByDao() { //EmployeeDao dao = new EmployeeDao(); //這裏須要bean配置,直接new是傻了,EmployeeDao類上 @Repository自動實例 EmployeeDao dao = ctx.getBean(EmployeeDao.class); Employee employee = dao.getEmployee(1); System.out.println(employee); //結果:Employee{id=1, lastName='sansan', firstName='tang', email='fenm@qq.com'} } /** * 查詢:實體類集合 * 不是調用的 queryForList */ public void TestQueryForList() { String sql = "select * from employee"; RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class); List<Employee> employee = jdbcTemplate.query(sql, rowMapper); System.out.println(employee); //[Employee{id=1, lastName='sansan', firstName='tang', email='fenm@qq.com'}, // Employee{id=2, lastName='li', firstName='xiao', email='denm@qq.com'}] } /** * 查詢:單個的值 */ public void testQueryForOne() { String sql = "select count(*) from employee"; Long count = jdbcTemplate.queryForObject(sql, Long.class); System.out.println(count); //2 } public void TestConnection() throws SQLException { DataSource dataSource = (DataSource) ctx.getBean("datasource"); System.out.println(dataSource.getConnection()); } }
Dao封裝方法
@Repository public class EmployeeDao { @Autowired private JdbcTemplate jdbcTemplate; public Employee getEmployee(int id) { String sql = "select * from employee where id = ?"; RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class); Employee employee =jdbcTemplate.queryForObject(sql,rowMapper,id); return employee; } }
調用
/** * 讀取一條數據,獲得對應的對象 * 配置Dao,調用Dao */ public void testQueryForObjectByDao() { //EmployeeDao dao = new EmployeeDao(); //這裏須要bean配置,直接new是傻了,EmployeeDao類上 @Repository自動實例 EmployeeDao dao = ctx.getBean(EmployeeDao.class); Employee employee = dao.getEmployee(1); System.out.println(employee); //結果:Employee{id=1, lastName='sansan', firstName='tang', email='fenm@qq.com'} }
bean
<!--配置namedParameterJdbcTemplate,該對象可以使用具名參數 其沒有無參構造器,因此必須制定構造器--> <bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> <constructor-arg ref="datasource"></constructor-arg> </bean>
實現
NamedParameterJdbcTemplate namedParameterJdbcTemplate = ctx.getBean(NamedParameterJdbcTemplate.class); /** * update(String sql, Map<String, ?> paramMap) * Map<String, ?> paramMap 能夠爲參數起名字。 * 1. 優勢:若多個參數,不用對應位置,直接對應參數名 * 2. 缺點:麻煩。 */ public void testNamedParameterJdbcTemplate() { String sql = "insert into employee(`last_name`, `first_name`, `email`) VALUES (:ln,:fn,:email) "; Map<String,Object> paramMap = new HashMap<>(); paramMap.put("ln","ww"); paramMap.put("fn","cc"); paramMap.put("email","wccw@qq.com"); namedParameterJdbcTemplate.update(sql,paramMap); } /** * 具名參數可使用 update(String sql, SqlParameterSource paramSource) * 1.SQL 語句中參數名和類屬性一致 * 2.使用 SqlParameterSource 的 BeanPropertySqlParameterSource 實現類做爲參數, * (:字段)具備setter與getter方法。 */ public void testNamedParameterJdbcTemplateWithParamSource() { String sql = "insert into employee(`last_name`, `first_name`, `email`) VALUES (:lastName,:firstName,:email) "; Employee employee = new Employee(); employee.setLastName("XYZ"); employee.setFirstName("XYZ"); employee.setEmail("XYZ@qq.com"); SqlParameterSource source = new BeanPropertySqlParameterSource(employee); namedParameterJdbcTemplate.update(sql,source); }
事務的四個關鍵屬性(ACID)
Repository層
public interface BookShopDao { //根據書號找單價 public int findBookPriceByIsbn(String isbn); //更新書的庫存(書號對應的庫存-1) public boolean updateBookStock(String isbn); //更新用戶帳戶餘額:username的 blance -price public boolean updateUserAccount(String username,int price); }
實現類 BookShopDao
@Repository public class BookShopDaoImpl implements BookShopDao { @Autowired private JdbcTemplate jdbcTemplate; @Override public int findBookPriceByIsbn(String isbn) { String sql = "select price from book where isbn = ?"; return jdbcTemplate.queryForObject(sql, Integer.class, isbn); } @Override public boolean updateBookStock(String isbn) { //檢查庫存是否不夠,若不夠則拋出異常 String sql2 = "select stock from book_stock where isbn = ?"; int stock = jdbcTemplate.queryForObject(sql2, Integer.class, isbn); if (stock == 0) { throw new BookShopException("庫存不足"); } else { String sql = "update book_stock set stock = stock-1 where isbn = ?"; return (jdbcTemplate.update(sql, isbn)) > 0; } } @Override public boolean updateUserAccount(String username, int price) { //驗證餘額不足 String sql2 = "select balance from account where username = ?"; int balance = jdbcTemplate.queryForObject(sql2, Integer.class, username); if (balance < price) { throw new AccountException("餘額不足"); } else { String sql = "update account set balance = balance-? where username = ?"; return (jdbcTemplate.update(sql, price, username)) > 0; } } }
service層
public interface BookShopService { //某人買一本書 boolean purchase(String isbn, String username); }
service實現
@Service public class BookShopServiceImpl implements BookShopService { @Autowired private BookShopDao bookShopDao; @Override public boolean purchase(String isbn, String username) { //1.獲取書單價 int price = bookShopDao.findBookPriceByIsbn(isbn); //2.更新書的庫存 bookShopDao.updateBookStock(isbn); //3.更新用戶餘額 return bookShopDao.updateUserAccount(username, price); } }
demo實現(沒有事務的狀況下,出現只扣庫存的操做)
public class TransactionDemo { static ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); static BookShopService bookShopService = ctx.getBean(BookShopService.class); public static void main(String[] args) { testpurchase(); } public static void testpurchase(){ System.out.println(bookShopService.purchase("0001","Tom")); } //餘額不足 }
爲了方便查看是哪一個方法的錯誤,實現了2個自定義異常,重寫RuntimeException
方法
public class AccountException extends RuntimeException{...} public class BookShopException extends RuntimeException{...}
bean
<!--配置事務管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="datasource"></property> </bean> <!--啓用事務註解 http://www.springframework.org/schema/tx--> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
在實現類添加@Transactional
@Service public class BookShopServiceImpl implements BookShopService { @Autowired private BookShopDao bookShopDao; //添加事務註解 @Transactional @Override public boolean purchase(String isbn, String username) { //1.獲取書單價 int price = bookShopDao.findBookPriceByIsbn(isbn); //2.更新書的庫存 bookShopDao.updateBookStock(isbn); //3.更新用戶餘額 return bookShopDao.updateUserAccount(username, price); } }
疑難:Spring @transaction不起做用,Spring事物注意事項
在須要事務管理的地方加@Transactional 註解。@Transactional 註解能夠被應用於接口定義和接口方法、類定義和類的 public 方法上 。
@Transactional 註解只能應用到 public 可見度的方法上 。 若是你在 protected、private 或者 package-visible 的方法上使用 @Transactional 註解,它也不會報錯, 可是這個被註解的方法將不會展現已配置的事務設置。
注意僅僅 @Transactional 註解的出現不足於開啓事務行爲,它僅僅 是一種元數據。必須在配置文件中使用配置元素,才真正開啓了事務行爲。
重點事項:
Spring事物是基於類和接口的(通俗理解即:在調用的時候不能再同一個類裏面被調用,必須調用外面的類去作事物操做)
Spring的事物必須是可見的(即:定義的方法必須是public的)
定義消費接口
public interface CashierService { public boolean checkout(String username,List<String> isbns); }
實現
@Service public class CashierServiceImp implements CashierService { @Autowired private BookShopService bookShopService; @Transactional @Override public boolean checkout(String username, List<String> isbns) { try { for (String isbn : isbns) { bookShopService.purchase(isbn,username); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } }
測試
static CashierService cashierService = ctx.getBean(CashierService.class); public static void main(String[] args) { tesCashier(); } public static void tesCashier() { List<String> list = Arrays.asList("0001", "0002"); System.out.println(cashierService.checkout("Tom",list)); }
默認傳播模式 propagation = Propagation.REQUIRES
若是同時買0001與0002的價格不夠,就不能購買成功。
如今該爲 REQUIRES_NEW
模式:
只要能購買成功0001商品,就能成功,後面不能購買時,報錯。
至關於purchase分爲一個小的事務。
併發致使的問題
隔離級別
//添加事務註解 //isolation 隔離級別,常取值 READ_COMMITTED //noRollbackFor = {AccountException.class} 對哪一個異常不回滾 ,聲明爲 Class[] 類型的 //rollbackFor 碰見必須回滾 ,聲明爲 Class[] 類型的 //readOnly = true 只讀事務屬性: 表示這個事務只讀取數據但不更新數據, 這樣能夠幫助數據庫引擎優化事務. //timeout(s) 指定強制回滾以前事務佔用時間(秒) @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT , noRollbackFor = {AccountException.class}, readOnly = true, timeout = 3 )
<?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" xmlns:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--引用外部文件db.properties--> <context:property-placeholder location="classpath:db.properties"/> <!--配置jdbc--> <bean id="datasource" class="org.apache.commons.dbcp2.BasicDataSource"> <property name="driverClassName" value="${drivername}"></property> <property name="url" value="${url}"></property> <property name="username" value="${user}"></property> <property name="password" value="${pass}"></property> </bean> <!--配置Spring的JdbcTemplate--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="datasource"></property> </bean> <!--配置Bean--> <bean id="bookShopDao" class="com.cnblogs.tangge.TransactionXML.BookShopDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean> <bean id="bookShopService" class="com.cnblogs.tangge.TransactionXML.BookShopServiceImpl"> <property name="bookShopDao" ref="bookShopDao"></property> </bean> <bean id="cashierService" class="com.cnblogs.tangge.TransactionXML.CashierServiceImp"> <property name="bookShopService" ref="bookShopService"></property> </bean> <!--1.配置事務管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="datasource"></property> </bean> <!--2.配置事務屬性--> <tx:advice id="txadvice" transaction-manager="transactionManager"> <tx:attributes> <!--根據方法取事務屬性--> <tx:method name="purchase" propagation="REQUIRES_NEW"/> <tx:method name="get*" read-only="true"/> <tx:method name="find*" read-only="true"/> <tx:method name="*"/> </tx:attributes> </tx:advice> <!--3.配置事務切入點,切入點及事務關聯起來--> <aop:config> <aop:pointcut id="purchase" expression="execution(* com.cnblogs.tangge.TransactionXML.BookShopServiceImpl.purchase(..))"/> <aop:advisor advice-ref="txadvice" pointcut-ref="purchase"></aop:advisor> </aop:config> <aop:config> <aop:pointcut id="checkout" expression="execution(* com.cnblogs.tangge.TransactionXML.CashierServiceImp.checkout(..))"/> <aop:advisor advice-ref="txadvice" pointcut-ref="checkout"></aop:advisor> </aop:config> </beans>