Spring框架采用的是分层架构,它一系列的功能要素被分为20个模块。
(1)Spring自身的jar包:
spring-beans-4.3.6.RELEASE.jar
spring-context-4.3.6.RELE2ASE.jar
spring-core-4.3.6.RELEASE.jar
spring-expression-4.3.6.RELEASE.jar
(2)导入记录日志的jar包 commons-logging-1.1.1.jar
(1)如果使用eclipse
,则创建xml文件,拷贝如下代码:
注意:beans 里面的内容叫做命名空间
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
</beans>
(2)如果使用STS
,则直接
① File
->New
->Spring Bean Configuration File
② 为文件取名字 例如:applicationContext.xml
package com.hkd.spring.mod;
public class Person {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + "]";
}
}
<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="personOne" class="com.hkd.spring.mod.Person">
<property name="id" value="1001">property>
<property name="name" value="小明">property>
bean>
<bean id="personTwo" class="com.hkd.spring.mod.Person">
<property name="id" value="1002">property>
<property name="name" value="小红">property>
bean>
beans>
代码提要:
getBean()
方法获取对象package com.hkd.spring.mod;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestBySpring {
public static void main(String[] args) {
// 1.初始化容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
// 2.通过getBean()方法获取对象(三种方法获取)
// 2.1 第一种
//Person person = (Person)ac.getBean("person");
// 2.2 第二种【使用此方法获取对象时,要求Spring所管理的此类型的对象只能有一个】
//Person person = ac.getBean(Person.class);
// 2.3 第三种【一般用这个方式,同时指定id和name】
Person person = ac.getBean("personOne", Person.class);
System.out.println(person);
}
}
ApplicationContext
:BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。在第三章Spring完成的简单案例中,applicationContext.xml文件里给Person类的id和name进行赋值的时候,用到了
标签,这种就是set方式注入
<bean id="personOne" class="com.hkd.spring.mod.Person">
<property name="id" value="1001">property>
<property name="name" value="小明">property>
bean>
(1)这里重新写一个类Student,给它几个属性,并完成他的构造方法和toString方法(Java基础内容,如果不会的话要补课咯!)
package com.hkd.spring.di;
public class Student {
private Integer id;
private String name;
private Integer age;
private String sex;
private double score;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", age=" + age + ", sex=" + sex + ", score=" + score + "]";
}
public Student(Integer id, String name, Integer age, String sex) {
super();
this.id = id;
this.name = name;
this.age = age;
this.sex = sex;
}
public Student(Integer id, String name, double score, String sex) {
super();
this.id = id;
this.name = name;
this.score = score;
this.sex = sex;
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
}
(2)构造方式注入
标签,提供里面的value值,会根据value值自动去实体类中找对应的构造方法,进行匹配
标签,但除了value属性,我们还给他一个index和type属性,意思是第index个属性的类型是type,这样可以更加精确地匹配
<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="s2" class="com.hkd.spring.di.Student">
<constructor-arg value="10011">constructor-arg>
<constructor-arg value="李四">constructor-arg>
<constructor-arg value="23">constructor-arg>
<constructor-arg value="男">constructor-arg>
bean>
<bean id="s3" class="com.hkd.spring.di.Student">
<constructor-arg value="10012">constructor-arg>
<constructor-arg value="王五">constructor-arg>
<constructor-arg value="90" index="2" type="double">constructor-arg>
<constructor-arg value="男">constructor-arg>
bean>
beans>
(3)编写main函数进行测试
package com.hkd.spring.di;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("beans-di.xml");
// 获取student对象
Student stu2 = ac.getBean("s2",Student.class);
System.out.println(stu2);
Student stu3 = ac.getBean("s3",Student.class);
System.out.println(stu3);
}
}
(4)最终结果
我们发现score被赋值了,说明构造方法注入成功!
Spring从2.5版本开始引入了一个新的p命名空间,可以通过
首先,我们需要在beans命名空间处加上一句话
xmlns:p=“http://www.springframework.org/schema/p”
直接在bean标签的属性中,添加 p:属性
的方式进行赋值
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="s4" class="com.hkd.spring.di.Student"
p:id="10013" p:name="赵六" p:age="10" p:sex="男"
p:score="100.0">
bean>
beans>
package com.hkd.spring.di;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("beans-di.xml");
Student stu4 = ac.getBean("s4",Student.class);
System.out.println(stu4);
}
}
得出结果
上面几个例子,我们赋值的时候,都是用value属性进行赋值的,value属性里面都是字符串,像这种的就叫做字面量注入。下面是字面量的定义:
如果value属性的值是一个引用类型,是一个类的话,它就不是字面量,就不能用value赋值了,这时候我们可以采用ref='bean的id'
属性来进行注入
package com.hkd.spring.di;
public class Teacher {
private Integer tid;
private String tname;
public Integer getTid() {
return tid;
}
public void setTid(Integer tid) {
this.tid = tid;
}
public String getTname() {
return tname;
}
public void setTname(String tname) {
this.tname = tname;
}
@Override
public String toString() {
return "Teacher [tid=" + tid + ", tname=" + tname + "]";
}
}
package com.hkd.spring.di;
public class Student {
private Integer id;
private String name;
private Integer age;
private String sex;
private double score;
private Teacher teacher; //定义一个Teacher类型的属性
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", age=" + age + ", sex=" + sex + ", score=" + score
+ ", teacher=" + teacher + "]";
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
}
ref="bean的id"
这种方式来注入bean
<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="s5" class="com.hkd.spring.di.Student">
<property name="id" value="10014">property>
<property name="name" value="傻七">property>
<property name="age" value="28">property>
<property name="sex" value="男">property>
<property name="teacher" ref="teacher">property>
bean>
<bean id="teacher" class="com.hkd.spring.di.Teacher">
<property name="tid" value="001">property>
<property name="tname" value="A老师">property>
bean>
beans>
package com.hkd.spring.di;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("beans-di.xml");
Student stu5 = ac.getBean("s5",Student.class);
System.out.println(stu5);
}
}
或
元素里,不需要设置任何id或name属性
<bean id="s6" class="com.hkd.spring.di.Student">
<property name="id" value="10014">property>
<property name="name" value="崔八">property>
<property name="age" value="38">property>
<property name="sex" value="女">property>
<property name="teacher">
<bean class="com.hkd.spring.di.Teacher">
<property name="tid" value="2222">property>
<property name="tname" value="admin">property>
bean>
property>
bean>
指定简单的常量值;通过
指定对其他Bean的引用;通过
指定内置bean定义;通过
指定空元素;甚至可以内嵌其他集合java.util.List
类型的属性,需要指定
标签,在标签里包含一些元素java.util.Set
需要使用
标签,定义的方法与List一样
元素,但数组还可以使用
标签(一)以字面量为值的list集合
package com.hkd.spring.di;
import java.util.List;
public class Teacher {
private Integer tid;
private String tname;
private List<String> cls; //增加属性cls班级
public List<String> getCls() {
return cls;
}
public void setCls(List<String> cls) {
this.cls = cls;
}
public Integer getTid() {
return tid;
}
public void setTid(Integer tid) {
this.tid = tid;
}
public String getTname() {
return tname;
}
public void setTname(String tname) {
this.tname = tname;
}
@Override
public String toString() {
return "Teacher [tid=" + tid + ", tname=" + tname + "]";
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="t1" class="com.hkd.spring.di.Teacher">
<property name="tid" value="111">property>
<property name="tname" value="张老师">property>
<property name="cls">
<list>
<value>A班value>
<value>B班value>
<value>C班value>
list>
property>
bean>
beans>
package com.hkd.spring.di;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("beans-di.xml");
Teacher t1 = ac.getBean("t1",Teacher.class);
System.out.println(t1);
}
}
(二)以bean的引用为值的list集合
<bean id="t2" class="com.hkd.spring.di.Teacher">
<property name="tid" value="222">property>
<property name="tname" value="李老师">property>
<property name="students">
<list>
<ref bean="s1">ref>
<ref bean="s2">ref>
<ref bean="s3">ref>
list>
property>
bean>
标签定义,
标签里可以使用多个
作为子标签。每个条目包含一个键和一个值
标签里定义键
、
、
或
元素<bean id="t3" class="com.hkd.spring.di.Teacher">
<property name="tid" value="333">property>
<property name="tname" value="王老师">property>
<property name="bossMap">
<map>
<entry>
<key>
<value>10001value>
key>
<value>校长value>
entry>
<entry>
<key>
<value>10002value>
key>
<value>副校长value>
entry>
map>
property>
bean>
标签在id为t1的bean的内部,这个
里的内容只能在t1这个bean中使用。如果我们想在其他的bean中也能使用,则就需要将这个集合bean的配置拿到外面util
名称空间1. 首先我们需要在beans的命名空间处添加上util的命名空间
xmlns:util=“http://www.springframework.org/schema/util”
xsi:schemaLocation=“http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.3.xsd”
2. 在xml文件里,使用
标签,里面的集合可以是字面量,也可以是引用,这个list就写在了bean的外部,在bean中的property标签直接使用ref属性调用即可
<bean id="t4" class="com.hkd.spring.di.Teacher">
<property name="tid" value="444">property>
<property name="tname" value="忽老师">property>
<property name="students" ref="students">property>
bean>
<util:list id="students">
<ref bean="s2"/>
<ref bean="s3"/>
<ref bean="s4"/>
util:list>
package com.hkd.spring.di;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("beans-di.xml");
Teacher t4 = ac.getBean("t4",Teacher.class);
System.out.println(t4);
}
}
、
、
标签,以map为例随便写一个 <util:map>
<entry>
<key>
<value>1value>
key>
<value>张三value>
entry>
util:map>
org.springframework.beans.factory.FactoryBean
接口package com.hkd.spring.factorybean;
public class Car {
private String brand;
private Double price;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public String toString() {
return "Car [brand=" + brand + ", price=" + price + "]";
}
}
getObject()
将创建好的bean返回给IOC容器、getObjectType()
返回bean的类型、isSingleton()
创建的bean是否单例package com.hkd.spring.factorybean;
import org.springframework.beans.factory.FactoryBean;
public class MyFactory implements FactoryBean<Car>{
@Override
public Car getObject() throws Exception {
Car car = new Car();
car.setBrand("奥迪");
car.setPrice(200000.0);
return car;
}
@Override
public Class<?> getObjectType() {
return Car.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
<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="factory" class="com.hkd.spring.factorybean.MyFactory">bean>
beans>
package com.hkd.spring.factorybean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("factory-bean.xml");
Object object = ac.getBean("factory");
System.out.println(object);
}
}
元素的scope
属性里设置bean的作用域,以决定这个bean是单实例的还是多实例的
<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="student" class="com.hkd.ioc.scope.Student" scope="singleton">
<property name="sid" value="1001">property>
<property name="sname" value="张三">property>
bean>
beans>
类别 | 说明 |
---|---|
singleton | 在SpringIOC容器里默认只存在一个bean实例,类型是单实例 |
prototype | 每次调用getBean()时,都会返回一个新的实例 |
request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 |
session | 同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的Bean,该作用域仅适用于WebApplicationContext环境 |
Spring IoC容器可以管理bean的生命周期,Spring允许在bean生命周期内特定的时间点执行指定的任务
bean的生命周期进行管理的过程:
① 通过构造器或工厂方法创建bean实例
② 为bean的属性设置值和对其他bean的引用
③ 调用bean的初始化方法
④ bean可以使用了
⑤ 当容器关闭时,调用bean的销毁方法
举例演示:
package com.hkd.ioc.life;
public class Person {
private Integer id;
private String sex;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
System.out.println("2.依赖注入");
this.id = id;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "4.使用";
}
//bean创建实例的时候会调用无参构造
public Person() {
System.out.println("1.创建对象");
}
//创建初始化方法
public void init() {
System.out.println("3.初始化");
}
//创建销毁方法
public void destroy() {
System.out.println("5.销毁");
}
}
init-method='方法名'
属性和destroy-method='方法名'
属性,具体使用方法如下
<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="person" class="com.hkd.ioc.life.Person" init-method="init" destroy-method="destroy">
<property name="id" value="1001">property>
<property name="sex" value="男">property>
<property name="name" value="张三">property>
bean>
beans>
package com.hkd.ioc.life;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("life.xml");
Person person = ac.getBean("person",Person.class);
System.out.println(person);
//关闭bean
ac.close();
}
}
bean后置处理器允许在调用初始化方法前后对bean进行额外的处理
bean后置处理器对IoC容器里的所有bean实例逐一处理,而非单一实例
bean后置处理器需要实现接口:
org.springframework.beans.factory.config.BeanPostProcessor
在初始化方法被调用前后,Spring将把每个bean实例分别传递给上述接口的以下两个方法:
添加bean后置处理器后bean的生命周期
① 通过构造器或工厂方法创建bean实例
② 为bean的属性设置值和对其他bean的引用
③ 将bean实例传递给bean后置处理器的postProcessBeforeInitialization()方法【新增】
④ 调用bean的初始化方法
⑤ 将bean实例传递给bean后置处理器的postProcessAfterInitialization()方法【新增】
⑥ bean可以使用了
⑦ 当容器关闭时调用bean的销毁方法
autowire属性值 | 说明 |
---|---|
default | 由 |
byName | 通过属性名和Spring容器中bean的id进行比较,若一致则可直接赋值 |
byType | 通过Spring容器中bean的类型,为兼容性的属性赋值 |
缺点:当设置autowire属性的时候,会作用于该bean中所有的非字面量属性,有些不需要用到的属性也用到了,因此一般都不用这两种属性
举例说明
package com.hkd.ioc.autowire;
public class Emp {
private Integer eid;
private String ename;
private Car car;
private Dept dept;
public Integer getEid() {
return eid;
}
public void setEid(Integer eid) {
this.eid = eid;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Emp [eid=" + eid + ", ename=" + ename + ", car=" + car + ", dept=" + dept + "]";
}
}
一般的注入方法(之前已讲)
<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="emp" class="com.hkd.ioc.autowire.Emp" autowire="byName">
<property name="eid" value="1001">property>
<property name="ename" value="张三">property>
<property name="car" ref="car">property>
<property name="dept" ref="dept">property>
bean>
<bean id="car" class="com.hkd.ioc.autowire.Car">
<property name="cid" value="666666">property>
<property name="cname" value="奔驰">property>
bean>
<bean id="dept" class="com.hkd.ioc.autowire.Dept">
<property name="did" value="1">property>
<property name="dname" value="开发部">property>
bean>
beans>
使用自动装配的byName属性进行装配
<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="emp" class="com.hkd.ioc.autowire.Emp" autowire="byName">
<property name="eid" value="1001">property>
<property name="ename" value="张三">property>
bean>
<bean id="car" class="com.hkd.ioc.autowire.Car">
<property name="cid" value="666666">property>
<property name="cname" value="奔驰">property>
bean>
<bean id="dept" class="com.hkd.ioc.autowire.Dept">
<property name="did" value="1">property>
<property name="dname" value="开发部">property>
bean>
beans>
使用自动装配的byType进行装配
<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="emp" class="com.hkd.ioc.autowire.Emp" autowire="byType">
<property name="eid" value="1001">property>
<property name="ename" value="张三">property>
bean>
<bean id="car" class="com.hkd.ioc.autowire.Car">
<property name="cid" value="666666">property>
<property name="cname" value="奔驰">property>
bean>
<bean id="dept" class="com.hkd.ioc.autowire.Dept">
<property name="did" value="1">property>
<property name="dname" value="开发部">property>
bean>
beans>
@Component
—— 标识一个受Spring IoC容器管理的组件(Bean),可以使用在任何层次,使用时只需将该注解标注在相应的类上即可
- @Component 是把最普通的类(不属于三层架构的类)实例化到spring容器中,相当于配置文件中的
- @Component(“userMod”),括号里给的参数相当于配置的 id 属性
@Repository
—— 标识一个受Spring IoC容器管理的持久化层组件(DAO层)
@Service
—— 标识一个受Spring IoC容器管理的业务逻辑层组件(Service层)
@Controller
—— 标识一个受Spring IoC容器管理的表述层控制器组件(控制层,处理请求和响应)
注意:
- 以上三个注解的功能完全相同,只是为了区分各个层而已,使代码更加清晰,在实际开发中,要在不同功能的类上加上相应的注解
- 组件的实质就是加上注解的类,也指Sping中管理的bean
@value("属性值")
—— 对一般属性进行注入,直接写在属性的上方,可以不提供set方法
@Autowired
—— 自动装配,在需要赋值的非字面量属性上,加上此注解,就可以在Spring容器中通过不同的方式匹配到相应的bean
注意:
- @Autowired 自动装配时默认byType(类型装配)的方式,此时要求Spring容器中只有一个能为其赋值
- 当默认的byType实现不了装配时,会自动切换到byName,此时要求Spring容器中有一个bean的id和属性名一致
- 若自动装配时,匹配到多个能够赋值的bean,可使用
@Qualifier(value="beanId")
指定使用的bean,具体使用方法如下:@Autowired
和@Qualifier(value="beanId")
可以一起作用于一个带形参的方法上,此时@Qualifier(value="beanId")
所指定的bean作用于形参
@Resource(name="")
—— 作用相当于@Autowired
和@Qualifier
的结合使用
- @Resource(name="") 默认会根据指定的name属性去Spring容器中寻找与该名称匹配的类型
- 例如:@Resource(name=“userDao”),只有当找不到与名称匹配的Bean时才会按照类型来装配注入
- 与@Autowired刚好相反,@Autowired是按照类型装配的,如果想按照名称进行装配还需要配合@Qualifier使用,而@Resource是按照名称装配的,名称找不到了才按照类型装配
spring-aop-4.3.6.RELEASE.jar
xmlns:context=“http://www.springframework.org/schema/context”
xsi:schemaLocation=“http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd”
标签,base-package的值为一个需要扫描的包@controller
package com.hkd.ioc.userMod.controller;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
public UserController() {
System.out.println("UserController");
}
}
@service
package com.hkd.ioc.userMod.service;
//定义UserService接口
public interface UserService {
}
package com.hkd.ioc.userMod.service;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService{
public UserServiceImpl() {
System.out.println("UserServiceImpl");
}
}
@Repository
package com.hkd.ioc.userMod.service;
//定义UserDao接口
public interface UserDao {
}
package com.hkd.ioc.userMod.dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDaoImpl implements UserDao{
public UserDaoImpl() {
System.out.println("UserDaoImpl");
}
}
标签
<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-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="com.hkd.ioc.userMod">context:component-scan>
beans>
package com.hkd.ioc.userMod;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("user.xml");
}
}
上两节我们介绍了
标签,我们知道这个标签的属性base-package需要写的是一个包的名,意味着扫描这一个包,上一个例子里扫描了UserMod包,意味着UserMod包下的所有包都扫描了
但实际开发中,往往不需要扫描所有的包,就得一个一个写UserMod.Service,UserMod.Dao等等,比较麻烦
这里介绍了
标签下的两个标签,可以在设定的包结构下再次通过注解和类型具体包含到某个或某几个类
<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-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.hkd.ioc.userMod" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="assignable" expression="com.hkd.ioc.UserMod.service.UserServiceImpl"/>
context:component-scan>
<context:component-scan base-package="com.hkd.ioc.UserMod">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
<context:exclude-filter type="assignable" expression="com.hkd.ioc.UserMod.service.UserServiceImpl"/>
context:component-scan>
beans>
面向对象是纵向继承机制
面向切面是横向抽取机制
在用AOP编程时,有些方法需要定义一些公共功能,这些公共功能是针对不同类需要做相同的事情而编写的,这些方法叫做横切关注点,把横切关注点抽取出来,放到一个类里,这样的类我们通常称之为切面
从每个方法中抽取出来的同一类非核心业务
将横切关注点封装成一个类,这个类就是切面
横切关注点在切面里的叫法,二者实际是一个东西,在不同位置叫法不同
被通知的对象,也就是抽取出的代码所作用到的对象
将通知应用到目标对象之后,被动态创建的对象
功能执行过程中的各个位置,一般只操作四个位置:方法调用前、方法调用后、当抛出异常和finally位置
org.springframework.aop.Pointcut
接口进行描述,它使用类和方法作为连接点的查询条件java.lang.reflect.Proxy
来实现举例说明:
首先创建一个MathI接口,里面提供了加减乘除的四个方法
package com.hkd.proxy;
public interface MathI {
//加
public int plus(int i, int j);
//减
public int sub(int i, int j);
//乘
public int mul(int i, int j);
//除
public int div(int i, int j);
}
创建接口的实现类MathImpl,并添加方法的具体实现
package com.hkd.proxy;
public class MathImpl implements MathI{
@Override
public int plus(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;
}
}
创建日志类MyLogger,现在想在执行加减乘除方法之前和之后,有一个记录日志的操作,分别记录两个数是多少,和结果是多少
package com.hkd.proxy;
public class MyLogger {
//方法执行前的记录
public static void before(String methodName, String args) {
System.out.println("method:"+methodName+",args:"+args);
}
//方法执行后的记录
public static void after(String methodName, Object result) {
System.out.println("method:"+methodName+",args:"+result);
}
}
创建代理类,注意看步骤
package com.hkd.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class ProxyUtil {
//声明目标类接口
private MathImpl mathImpl;
public ProxyUtil(MathImpl mathImpl) {
super();
this.mathImpl = mathImpl;
}
//创建代理方法
public Object getProxy() {
//1. 获取当前类的类加载器,用来加载代理对象所属类
ClassLoader loader = this.getClass().getClassLoader();
//2. 获取目标对象实现的所有接口的class,代理类会和目标类实现相同的接口,最终通过代理对象实现功能
Class[] interfaces = mathImpl.getClass().getInterfaces();
//3. 使用代理类,进行增强,返回的是代理后的对象。Proxy.newProxyInstance(类加载器,接口,匿名内部类)
return Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
//所有动态代理的方法调用,都会交由invoke()方法去处理
//proxy:被代理后的对象
//method:将要被执行的方法信息
//args:执行方法时需要的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//通过代理对象实现功能,这时候就可以在实现功能前后加入一些其他的操作,比如下面的before和after方法
MyLogger.before(method.getName(), Arrays.toString(args));
//动态代理对象实现功能
Object result = method.invoke(mathImpl, args);
MyLogger.after(method.getName(), result);
return result;
} catch (Exception e) {
MyLogger.throwing();
e.printStackTrace();
}
return null;
}
});
}
}
创建测试类,进行测试
package com.hkd.proxy;
public class Test {
public static void main(String[] args) {
ProxyUtil proxy = new ProxyUtil(new MathImpl());
MathI math = (MathI)proxy.getProxy();
//执行plus方法
int i = math.plus(1, 1);
}
}
1. 导入jar包
在Spring基础5个包的基础上导入以下jar包
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aop-4.3.6.RELEASE.jar
spring-aspects-4.3.6.RELEASE.jar
如果不适用接口,则使用cglib动态代理,导入com.springsource.net.sf.cglib-2.2.0.jar
2. 添加命名空间
xmlns:aop=“http://www.springframework.org/schema/aop”
xsi:schemaLocation=http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
1. 格式:
execution( [权限修饰符] [返回值类型] [简单类名/全类名] [方法名] ([参数列表]) )
2. 作用:
通过表达式的方式定位一个或多个具体的连接点
3. 举例:
- execution(public void com.hkd.study.aop.plus(int, int))
意思是:访问修饰符为public,返回值是void类型,aop接口声明的plus方法,并且方法需要两个int类型的参数- execution( * com.hkd.study.aop.*.*(..))
a. 第一个*表示任意修饰符及任意返回值;
b. 第二个*表示类名,使用*代表所有的类;
c. 第三个*表示方法名,使用*代表所有方法;最后的(..)表示任意数量、类型的参数
d. 注意第一个*与包名直接有一个空格
4. 注意:
在AspectJ中,切入点表达式可以通过 “&&”、"||"、"!"等操作符结合起来,例如:
- execution (* *.add(int,..)) || execution(* *.sub(int,..))
表示任意类中第一个参数为int类型的add方法或sub方法- !execution (* *.add(int,..))
表示匹配不是任意类中第一个参数为int类型的add方法
1. 连接点
JoinPoint
接口的实例对象中2. 常用的方法
joinpoint.getArgs()
—— 获取实际参数数组
joinpoint.getSignature().getName()
—— 获取方法名
注解名称 | 描述 |
---|---|
@Aspect | 用于定义一个切面 |
@PointCut | 用于定义一个切入点表达式。使用时还需要定义一个包含名字和任意参数的方法签名来表示切入点表达式。实际上这个方法签名就是一个返回值为void且方法体为空的普通方法 |
通知名称 | 通知标识 | 描述 |
---|---|---|
前置通知 | @Before | 作用于方法执行之前需要指定value属性,该属性值用于指定一个切入点表达式 |
后置通知 | @After | 作用于方法的finally语句块,后置通知是在连接点完成之后执行的,即不管有没有异常,都会执行 |
返回通知 | @AfterReturning | 作用于方法执行之后,可通过returning设置接受方法返回值的变量名,要想在方法中使用,必须要方法的形参中设置和变量名相同的参数名的参数 |
异常通知 | @AfterThrowing | 作用于方法抛出异常时,可通过throwing设置接收方法返回的异常信息在参数列表中,通过具体的异常类型,来对指定的异常信息进行操作 |
环绕通知 | @Around | 能够全面地控制连接点,甚至可以控制是否执行连接点。连接点的参数类型必须是ProceedingJoinPoint,需要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法 |
以上通知的演示在5.4.6节的案例里
1. 仍然以加减乘除为例,演示各类通知,首先编写MathI接口
package com.hkd.AspectJ;
public interface MathI {
public int plus(int i, int j);
public int sub(int i, int j);
public int mul(int i, int j);
public int div(int i, int j);
}
2. 编写接口的实现类,注意注解的运用
package com.hkd.AspectJ;
import org.springframework.stereotype.Component;
@Component()
public class MathImpl implements MathI{
@Override
public int plus(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;
}
}
3. 编写日志类,解析各种通知的写法
package com.hkd.AspectJ;
import java.util.Arrays;
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;
@Aspect //标注当前类为切面
@Component
public class MyloggerAspect {
/**
* @Before:将方法指定为前置通知
* 必须设置value,其值为切入点表达式
* 前置通知,作用于方法执行之前
*/
@Before(value = "execution(* com.hkd.AspectJ.*.*(..))")
public void beforeMethod(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs(); //获取方法的参数
String methodName = joinPoint.getSignature().getName(); //获取方法名
System.out.println("method:"+methodName+",arguments:"+Arrays.toString(args));
}
/**
* @After:将方法标注为后置通知
* 作用于方法的finally语句块,即不管有没有异常都会执行
*/
@After(value = "execution(* com.hkd.AspectJ.*.*(..))")
public void afterMethod() {
System.out.println("后置通知");
}
/**
* @AfterReturning:将方法标注为返回通知
* 作用于方法执行之后
* 可通过returning设置接受方法返回值的变量名
* 要想在方法中使用,必须要方法的形参中设置和变量名相同的参数名的参数
*/
@AfterReturning(value = "execution(* com.hkd.AspectJ.*.*(..))",returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("method:"+methodName+",result:"+result);
}
/**
* @AfterThrowing:将方法标注为异常通知(例外通知)
* 作用于方法抛出异常时
* 可通过throwing设置接收方法返回的异常信息
* 在参数列表中,通过具体的异常类型,来对指定的异常信息进行操作
*/
@AfterThrowing(value = "execution(* com.hkd.AspectJ.*.*(..))",throwing = "ex")
public void afterThrowingMethod(Exception ex) {
System.out.println("有异常了:" + ex);
}
@Around(value="execution(* com.hkd.AspectJ.*.*(..))")
public Object arountMethod(ProceedingJoinPoint joinPoint) {
Object result = null;
try {
//前置通知
System.out.println("前置通知");
result = joinPoint.proceed(); //执行方法
//返回通知
System.out.println("返回通知");
return result;
} catch (Throwable e) {
//异常通知
System.out.println("异常通知");
e.printStackTrace();
} finally {
//后置通知
System.out.println("后置通知");
}
return -1;
}
}
4. 编xml文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/bean http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="com.hkd.AspectJ">context:component-scan>
<aop:aspectj-autoproxy />
beans>
5. 编写测试类,进行测试
package com.hkd.AspectJ;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("aop.xml");
MathI math = ac.getBean("mathImpl", MathI.class);
int i = math.div(2, 1);
System.out.println(i);
}
}
@Pointcut
注解将一个切入点声明成简单的方法package com.hkd.AspectJ;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect //标注当前类为切面
@Component
public class MyloggerAspect {
@Pointcut(value = "execution(* com.hkd.AspectJ.*.*(..))")
public void test() {}
@Before(value = "test()") //重用切入点
public void beforeMethod(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs(); //获取方法的参数
String methodName = joinPoint.getSignature().getName(); //获取方法名
System.out.println("method:"+methodName+",arguments:"+Arrays.toString(args));
}
}
@Order
注解指定@Order
注解,值越小优先级越高,@Order(1)
比@Order(2)
的优先级高,默认值为int的最大值1. 编写接口和实体类
package com.hkd.AspectJ_xml;
public interface MathI {
public int plus(int i, int j);
public int sub(int i, int j);
public int mul(int i, int j);
public int div(int i, int j);
}
package com.hkd.AspectJ_xml;
import org.springframework.stereotype.Component;
@Component()
public class MathImpl implements MathI{
@Override
public int plus(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;
}
}
2. 编写切面类
package com.hkd.AspectJ_xml;
import org.springframework.stereotype.Component;
@Component
public class MyLogger {
public void Before() {
System.out.println("前置通知");
}
public void AfterReturning() {
System.out.println("后置通知");
}
public void Around() {
System.out.println("环绕通知");
}
public void AfterThrowing() {
System.out.println("异常通知");
}
public void After() {
System.out.println("最终通知");
}
}
3. 配置xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="com.hkd.AspectJ_xml"></context:component-scan>
<aop:config>
<!-- 1. 配置切面 -->
<aop:aspect ref="myLogger">
<!-- 2. 配置切入点 -->
<aop:pointcut expression="execution(* com.hkd.AspectJ_xml.*.*(..))" id="cut"/>
<!-- 3. 配置通知 -->
<!-- 配置前置通知 -->
<aop:before method="Before" pointcut-ref="cut"/>
<!-- 配置后置通知 -->
<aop:after-returning method="AfterReturning" pointcut-ref="cut" returning="returnVal"/>
<!-- 配置环绕通知 -->
<aop:around method="Around" pointcut-ref="cut"/>
<!-- 配置异常通知 -->
<aop:after-throwing method="AfterThrowing" pointcut-ref="cut" throwing="e"/>
<!-- 配置最终通知 -->
<aop:after method="After" pointcut-ref="cut"/>
</aop:aspect>
</aop:config>
</beans>
4. 编写测试类
package com.hkd.AspectJ_xml;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("AspectJ_xml.xml");
MathI math = ac.getBean("mathImpl",MathI.class);
int i = math.plus(1, 3);
System.out.println(i);
}
}
在Spring基础5个包的基础上导入以下jar包:
spring-jdbc-4.3.6.RELEASE.jar
spring-orm-4.3.6.RELEASE.jar
spring-tx-4.3.6.RELEASE.jar
还需要导入数据库连接相关包:
mysql-connector-java-5.1.37-bin.jar
使用druid连接池:druid-1.1.9.jar
使用c3p0连接池:c3p0-0.9.5.5.jar
,mchange-commons-java-0.2.19.jar
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm
jdbc.username=root
jdbc.password=123
如果使用的是druid连接池,这样创建连接池
<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-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:property-placeholder location="db.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}">property>
<property name="url" value="${jdbc.url}">property>
<property name="username" value="${jdbc.username}">property>
<property name="password" value="${jdbc.password}">property>
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource">property>
bean>
beans>
如果使用的是c3p0连接池,这样创建连接池
<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-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}">property>
<property name="jdbcUrl" value="${jdbc.url}">property>
<property name="user" value="${jdbc.username}">property>
<property name="password" value="${jdbc.password}">property>
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource">property>
bean>
beans>
update()方法可以完成增删改操作,其返回值是int类型,返回受影响的行数
创建一个实体类Emp,其中有属性,并且在MySQL数据库中创建相应的表
package com.hkd.jdbctemplate;
public class Emp {
private Integer eid;
private String ename;
private Integer age;
private String sex;
public Integer getEid() {
return eid;
}
public void setEid(Integer eid) {
this.eid = eid;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Emp [eid=" + eid + ", ename=" + ename + ", age=" + age + ", sex=" + sex + "]";
}
}
使用c3p0数据库连接池进行数据库连接,详见6.2的步骤
使用Junit测试,进行update()的测试
package com.hkd.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
public class TestJdbcTemplate {
ApplicationContext ac = new ClassPathXmlApplicationContext("jdbc_c3p0.xml");
JdbcTemplate jdbcTemplate = ac.getBean("jdbcTemplate",JdbcTemplate.class);
@Test
public void testUpdate() {
//增删改函数(这里以增加为例)
String sql = "insert into emp values(null,?,?,?)";
//单条数据的增删改
jdbcTemplate.update(sql, "Jack", 24, "Women");
}
}
batchUpdate(String, List
执行批量增删改操作 @Test
public void testBatchUpdate() {
// 批量执行增删改的方法(这里以批量增加为例)
String sql = "insert into emp values(null,?,?,?)";
List<Object[]> list = new ArrayList<>();
list.add(new Object[] {"aa",11,"Woman"});
list.add(new Object[] {"bb",22,"Woman"});
list.add(new Object[] {"cc",33,"Woman"});
jdbcTemplate.batchUpdate(sql, list);
}
queryForObject(String, RowMapper\, Object...)
用来获取单条数据,返回一个对象@Test
public void testQueryForObjectReturnSingleObject() {
//queryForObject(sql, rowMapper, args) 用来获取单条数据,返回一个对象
String sql = "select eid,ename,age,sex from emp where eid = ?";
//将列名(字段名或字段名的别名)与属性名进行映射
RowMapper<Emp> rowMapper = new BeanPropertyRowMapper<>(Emp.class);
Emp emp = jdbcTemplate.queryForObject(sql, rowMapper, 1);
System.out.println(emp);
}
queryForObject(String, Class, Object...)
@Test
public void testQueryForObjectReturnSingleValue() {
//jdbcTemplate.queryForObject(sql, requiredType) 用来获取单个的值
String sql = "select count(eid) from emp";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println(count);
}
查询多条数据返回多个对象
@Test
public void testQuery() {
String sql = "select eid,ename,age,sex from emp";
RowMapper<Emp> rowMapper = new BeanPropertyRowMapper<>(Emp.class);
List<Emp> list = jdbcTemplate.query(sql, rowMapper);
for(Emp emp : list) {
System.out.println(emp);
}
}
package com.hkd.jdbc.dao;
import com.hkd.jdbctemplate.Emp;
public interface EmpDao {
public int insertEmp(Emp emp);
}
package com.hkd.jdbc.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.hkd.jdbctemplate.Emp;
@Repository
public class EmpDaoImpl implements EmpDao{
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public int insertEmp(Emp emp) {
String sql = "insert into emp values(null,?,?,?)";
Object[] obj = new Object[] {
emp.getEname(),emp.getAge(),emp.getSex()
};
return jdbcTemplate.update(sql, obj);
}
}
<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-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="com.hkd.jdbc.dao">context:component-scan>
<context:property-placeholder location="classpath:db.properties" />
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}">property>
<property name="jdbcUrl" value="${jdbc.url}">property>
<property name="user" value="${jdbc.username}">property>
<property name="password" value="${jdbc.password}">property>
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource">property>
bean>
beans>
package com.hkd.jdbc.dao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.hkd.jdbctemplate.Emp;
public class test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("jdbc_dao.xml");
EmpDao empDao = ac.getBean("empDaoImpl",EmpDao.class);
Emp emp = new Emp();
emp.setEid(8);
emp.setEname("zhangsan");
emp.setAge(80);
emp.setSex("Man");
int num = empDao.insertEmp(emp);
if(num>0)
System.out.println("插入"+num+"条数据成功");
else
System.out.println("插入数据失败");
}
}
属性 | 属性解释 |
---|---|
原子性(atomicity) | “原子”的本意是“不可再分”,事务的原子性表现为一个事务中涉及到的多个操作在逻辑上缺一不可。事务的原子性要求事务中的所有操作要么都执行,要么都不执行 |
一致性(consistency) | “一致”指的是数据的一致,具体是指:所有数据都处于满足业务规则的一致性状态。一致性原则要求:一个事务中不管涉及到多少个操作,都必须保证事务执行之前数据是正确的,事务执行之后数据仍然是正确的。如果一个事务在执行的过程中,其中某一个或某几个操作失败了,则必须将其他所有操作撤销,将数据恢复到事务执行之前的状态,这就是回滚 |
隔离性(isolation) | 在应用程序实际运行过程中,事务往往是并发执行的,所以很有可能有许多事务同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。隔离性原则要求多个事务在并发执行过程中不会互相干扰 |
持久性(durability) | 持久性原则要求事务执行完成后,对数据的修改永久的保存下来,不会因各种系统错误或其他意外情况而受到影响。通常情况下,事务对数据的修改应该被写入到持久化存储器中 |
1. 在原有的5个基本包的基础上导入以下jar包
AspectJ和aop相关包:
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aop-4.3.6.RELEASE.jar
事务相关包:
spring-tx-4.3.6.RELEASE.jar
2. 添加tx命名空间
xmlns:tx=“http://www.springframework.org/schema/tx”
xsi:schemaLocation=http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
3. 配置文件需要配置事务管理器
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
A方法和B方法都有事务,当A在调用B的时候,会将A中的事务传播给B方法,B方法对于事务的处理方式就是事务的传播行为
属性名称 | 属性值 | 属性解释 |
---|---|---|
PROPAGATION_REQUIRED | REQUIRED | 如果有事务在运行,当前的方法就在这个事务中运行,否则启动新事务,再执行方法 |
PROPAGATION_REQUIRES_NEW | REQUIRES_NEW | 如果方法已在事务环境中,则停止该事务,使用自己的事务; 如果方法不在事务环境中,则自动启动一个新事务后再执行方法 |
PROPAGATION_SUPPORTS | SUPPORTS | 如果当前方法处于事务环境中,则使用该事务,否则不使用事务 |
PROPAGATION_NOT_SUPPORTED | NOT_SUPPORTED | 当前方法不应该运行在事务中,如果有则将它挂起 |
PROPAGATION_MANDATORY | MANDATORY | 当前方法必须运行在事务内部,如果没有运行的事务,就抛出异常 |
PROPAGATION_NEVER | NEVER | 当前方法不应该运行在事务中,如果有运行的事务,就抛出异常 |
PROPAGATION_NESTED | NESTED | 即使当前方法处于事务中,依然会启动一个新事务,并且方法在嵌套的事务中执行; 即使当前执行的方法不在事务中,也会启动一个新事务,然后执行该方法 |
1. 注解方式
直接在要处理事务的类或者方法前,加上注解,注解中括号里添加属性propagation,如下
@Transactional(propagation = Propagation.REQUIRES_NEW)
1. 脏读:
① A将某条记录的age值从20修改为30
② B读取了A更新后的值:30
③ 现在A回滚了,age值恢复到了20
④ B读取到的30就是一个无效的值
2. 不可重复读
① A读取了age值为20
② B将age值修改为30
③ A再次读取age值成了30,和第一次读取不一致了
3. 幻读
① A读取了student表中的一部分数据
② B向student表中插入了新的行
③ A再读取student表时,就多出了一些行
数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。一个事务与其他事务隔离的程度称为隔离级别
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE
能力 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ UNCOMMITTED | √ | √ | √ |
READ COMMITTED | × | √ | √ |
REPEATABLE READ | × | × | √ |
SERIALIZABLE | × | × | × |
1. 注解方式
直接在@Transactional的isolation属性中设置隔离级别
@Transactional(isolation = Isolation.DEFAULT)
timeout:在事务强制回滚前,最多可以执行(等待)的时间
readOnly:指定当前事务中的一系列操作是否为只读;
- 如果设置为只读,不管事务中有没有写的操作,MySQL都会在请求访问数据的时候,不加锁,提高性能;
- 如果有写的操作,建议一定不能设置只读
1. 开启事务注解
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
2. 在需要使用事务的bean类或者bean类方法上使用注解@Transactional
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
<aop:config>
<aop:pointcut
expression="execution(* com.atguigu.tx.component.service.BookShopServiceImpl.purchase(..))"
id="txPointCut"/>
<aop:advisor advice-ref="myTx" pointcut-ref="txPointCut"/>
aop:config>
<tx:advice id="myTx" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="find*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="purchase"
isolation="READ_COMMITTED"
no-rollback-for="java.lang.ArithmeticException,java.lang.NullPointerException"
propagation="REQUIRES_NEW"
read-only="false"
timeout="10"/>
tx:attributes>
tx:advice>