把如下 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>