一、Spring概述
Spring是一个框架,也是一个容器。容器中存放了Java对象。
作用:减轻对项目模块之间的管理,类和类之间的管理,帮助开发人员创建对象,管理对象之间的关系。
核心技术:ioc和aop。能实现模块之间,类之间的解耦合。
依赖:类a中使用了类b的属性或方法,叫做类a依赖类b。
优点:轻量、解耦合、aop编程支持、方便集成其他框架
spring默认创建对象
spring默认创建对象的时间:是创建spring的容器的时候创建配置文件中所有的对象。
spring默认创建对象的方法:默认调用无参构造。
Spring存放哪些对象
放入Spring容器中:dao类,service类,controller类,工具类。Spring中的对象都是单例的,在容器中叫这个名字的对象只有一个。
不放入Spring容器中:实体类对象,Servlet,监听器,过滤器。
如何把对象放入容器
1、使用xml配置文件
2、使用注解
使用
1、使用maven,加入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
2、创建接口,并在当前包下创建子包,实现此接口
3、在resources下创建spring config的配置文件,文件名自定义
<?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">
<!--告诉Spring创建对象
声明bean,告诉spring要创建某个类的对象
id:对象的自定义名称,唯一值。spring通过这个名称找到对象
class:类的全限定名称,不能是接口。因为Spring是反射机制创建接口,必须使用类。
Spring把创建好的对象放在Map中,spring框架中会有map存放对象。id值作key,value是对象
例如:springMap.put("someService",new SomeServiceImpl());
一个bean标签声明一个对象
-->
<bean id="someService" class="com.daihan.service.impl.SomeServiceImpl"/>
<bean id="someService1" class="com.daihan.service.impl.SomeServiceImpl"/>
<!--Spring创建一个非自定义类的对象-->
<bean id="myDate" class="java.util.Date"/>
</beans>
<!--
spring的配置文件
1、beans:是根标签,spring中把java对象称为bean
2、spring-beans.xsd是约束文件,和Mybatis指定 dtd一样
-->
4、test目录下实现,通过ApplicationContext接口和它的实现类ClassPathXmlApplicationContext
实现一
@Test
public void test02(){
//使用spring容器创建对象
//1、指定spring配置文件的名称
String config="beans.xml";
//2、创建表示spring容器的对象,ApplicationContext
//ApplicationContext 表示spring容器,通过容器获取对象
//ClassPathXmlApplicationContext 表示从类路径(target/class/)中加载spring的配置文件
//此时 创建容器,spring创建对象
ApplicationContext ac=new ClassPathXmlApplicationContext(config);
//从容器中获取某个对象,需调用对象的方法
//getBean("配置文件中的bean的id"),返回值是Object
SomeService service= (SomeService) ac.getBean("someService");
//使用spring创建好的对象
service.doSome();
}
实现二:获取Spring容器中 Java对象的信息(数量,名称)
@Test
public void test03(){
String config="beans.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(config);
//获取容器中定义的对象数量
int count = ac.getBeanDefinitionCount();
System.out.println("容器中对象数量"+count);
//获取容器中定义的对象名称
String[] names = ac.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
}
二、IoC控制反转
控制反转:IoC是一种思想,指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。实质就是对象控制权的转移,通过外部容器实现对象的创建、属性赋值、依赖管理。
作用:使用IoC减少对代码的改动,也能实现不同的功能。实现解耦合。
控制:创建对象,对象属性的赋值,对象之间的关系管理。
反转:把创建对象的权限转移给代码之外的容器实现。
(容器:是一个服务器软件,一个框架例如spring)
(正转:由开发人员在代码中使用new构造方法创建对象,开发人员主动管理对象)
IoC的体现:例如Tomcat
IoC技术实现:DI (Dependency Injection)依赖注入,只需要在程序中提供要使用的对象名称就可以,至于如何在容器中创建、赋值、查找都是由容器内部实现。
Spring是使用DI实现IoC的功能,底层创建对象使用的是反射机制。(Spring是一个容器,管理对象,给属性赋值,底层是反射机制创建对象)
给Java对象属性赋值
在Spring的配置文件中,给java对象属性赋值
di:依赖注入,表示创建的对象,给属性赋值
di实现语法:
1、在spring配置文件,使用标签和属性完成,叫做基于xml的di实现
2、使用spring中的注解,完成属性赋值,叫做基于注解的di实现
di语法分类
1、set注入(设值注入):spring调用类的set方法,在set方法可以实现属性的赋值。(使用广泛)
2、构造注入,spring调用类的有参构造方法,创建对象。
实现步骤:
1、创建maven项目
2、加入maven依赖
spring的依赖,版本5.2.5
junit依赖
3、创建类(接口和他的实现类)
和没有使用框架一样,就是普通的类
4、 创建Spring需要使用的配置文件
声明类的信息,这些类由Spring创建和管理
通过spring语法完成属性的赋值
5、测试Spring创建对象
总结: 经常改的就用配置文件,不经常修改的就用注解。注解为主,配置文件辅助。
基于配置文件(set设值注入)
Spring底层调用setxxx()方法,给属性赋值。例如它利用配置文件中 name=“age” 推测类中有setName方法,从而调用这个方法。所以无论Student类中是否有name属性,只要有setName方法就能成功调用。
简单类型的set注入
Student类定义
public class Student {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
配置文件(src/main/resources目录下)
<?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">
<!--声明Student对象
di:给属性赋值
1、set注入
(spring调用setxxx()方法,给属性赋值。利用name="age"推测类中有setName方法,从而调用这个方法)
1)简单类型的set注入
<bean id="xx" class="yyy">
一个property只能给一个属性赋值,多个属性多个property
<property name="属性名字" value="属性值"/>
</bean>
-->
<bean id="myStudent" class="com.daihan.ba01.Student">
<property name="age" value="20"/><!--setName("李四")-->
<property name="name" value="李四"/>
</bean>
</beans>
引用类型的set注入
类
Student中涉及School类,School类省略
public class Student {
private String name;
private int age;
//引用类型
private School school;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setSchool(School school) {
System.out.println("setSchool:+school");
this.school = school;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", school=" + school +
'}';
}
}
配置文件
<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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--声明Student对象
di:给属性赋值
1、set注入
(spring调用setxxx()方法,给属性赋值。利用name="age"推测类中有setName方法,从而调用这个方法)
1)简单类型的set注入
<bean id="xx" class="yyy">
一个property只能给一个属性赋值,多个属性多个property
<property name="属性名字" value="属性值"/>
</bean>
2)引用类型的set注入
<bean id="xxx" class="yyy">
<property name="属性名称" ref="bean的id(对象名称)">
</bean>
-->
<bean id="myStudent" class="com.daihan.ba02.Student">
<property name="age" value="20"/>
<property name="name" value="李四"/>
<!--引用类型-->
<property name="school" ref="mySchool"/>
</bean>
<!--声明School对象-->
<bean id="mySchool" class="com.daihan.ba02.School">
<property name="name" value="北京大学"/>
<property name="address" value="北京海淀区"/>
</bean>
</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"
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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--声明Student对象
di:给属性赋值
1、set注入
(spring调用setxxx()方法,给属性赋值。利用name="age"推测类中有setName方法,从而调用这个方法)
1)简单类型的set注入
<bean id="xx" class="yyy">
一个property只能给一个属性赋值,多个属性多个property
<property name="属性名字" value="属性值"/>
</bean>
2)引用类型的set注入
<bean id="xxx" class="yyy">
<property name="属性名称" ref="bean的id(对象名称)">
</bean>
2、构造注入
(Spring调用类的有参构造方法,在创建对象的同时完成赋值)
使用:<constructor-arg>标签,一个<constructor-arg>表示构造方法一个参数。
<constructor-arg>标签属性:
name:表示构造方法的形参名
index:表示构造方法参数位置,从左往右,0、1、
value:构造方法形参是简单类型,使用value
ref:构造方法形参是引用类型,使用ref
-->
<!--使用name定位属性,给属性赋值-->
<bean id="myStudent" class="com.daihan.ba03.Student">
<constructor-arg name="name" value="Kramer"/>
<constructor-arg name="age" value="20"/>
<constructor-arg name="school" ref="mySchool"/>
</bean>
<!--使用index定位属性,给属性赋值(推荐)-->
<bean id="myStudent2" class="com.daihan.ba03.Student">
<constructor-arg index="0" value="Lee"/>
<constructor-arg index="1" value="22"/>
<constructor-arg index="2" ref="mySchool"/>
</bean>
<!--省略index-->
<bean id="myStudent3" class="com.daihan.ba03.Student">
<constructor-arg value="Lee"/>
<constructor-arg value="22"/>
<constructor-arg ref="mySchool"/>
</bean>
<!--声明School对象-->
<bean id="mySchool" class="com.daihan.ba03.School">
<property name="name" value="北京大学"/>
<property name="address" value="北京海淀区"/>
</bean>
</beans>
自动注入byName、byType
引用类型才有自动注入
配置文件
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--自动注入
Spring框架根据某些规则可以给”引用类型“赋值
常用的是:byName,byType
1、byName 按名称注入:java类中引用类型的属性名和Spring容器中(配置文件)<bean>的id名称一样,
且数据类型一致,这样容器中的bean就能赋值给引用类型。
语法:<bean id="xx" class="yyy" autowire="byName">
简单类型赋值...
//写完简单类型就行,引用类型Spring会根据名字去找
</bean>
2、byType 按类型注入:java类中引用类型的数据类型和Spring容器中(配置文件)<bean>的class属性是同源关系,
这些的bean能够赋值给引用数据类型。
同源:一类的意思。1、Java类中引用类型的数据类型和bean的class的值是一样的
2、Java类中引用类型的数据类型和bean的class的值是父子的关系
3、Java类中引用类型的数据类型和bean的class的值是接口和实现类的关系
语法:<bean id="xx" class="yyy" autowire="byType">
简单类型赋值...
//写完简单类型就行,引用类型Spring会根据名字去找
</bean>
-->
<!--byName示例-->
<bean id="myStudent" class="com.daihan.ba04.Student" autowire="byName">
<property name="age" value="20"/>
<property name="name" value="李四"/>
</bean>
<!--byType示例-->
<bean id="myStudent" class="com.daihan.ba05.Student" autowire="byType">
<property name="age" value="20"/>
<property name="name" value="张三"/>
</bean>
<!--声明School对象-->
<bean id="school" class="com.daihan.ba04.School">
<property name="name" value="清华大学"/>
<property name="address" value="北京海淀区"/>
</bean>
</beans>
测试代码省略
多配置文件的方式
优点:
1、文件大小得到控制,打开关闭效率高。
2、避免多人竞争带来的冲突。
分配方式:
1、按功能模块分类
2、按类的功能分类,数据库相关分一块,事务处理分一块,service功能分一块
注:使用通配符*时,必须保证配置文件在resources下同一文件夹中
示例:(配置文件建议在resources下同一文件夹中)
主配置文件
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--
包含关系的配置文件
Spring-total表示主配置文件,用于包含其他配置文件,主配置文件一般不定义对象
语法:<import resource="其他配置文件的类路径"/>
关键字:"classpath:" 表示类路径(class文件所在目录,target/class目录下),在Spring配置文件中要指定其他文件的位置,
则需要使用classpath告诉Spring到哪加载文件。
-->
<!--方法一:加载文件列表-->
<!--
<import resource="classpath:ba06/Spring-School.xml"/>
<import resource="classpath:ba06/Spring-Sutdent.xml"/>
-->
<!--方法二:在包含关系的配置文件中,可以通配符(*:表示任意字符)-->
<import resource="classpath:ba06/Spring-*.xml"/>
</beans>
Student配置文件
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--Student模块所有bean声明-->
<bean id="myStudent" class="com.daihan.ba06.Student" autowire="byType">
<property name="age" value="30"/>
<property name="name" value="张三"/>
</bean>
</beans>
School配置文件
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--School模块bean的声明-->
<bean id="myschool" class="com.daihan.ba06.School">
<property name="name" value="航空大学"/>
<property name="address" value="北京海淀区"/>
</bean>
</beans>
测试代码
public class MyTest06 {
@Test
public void test02(){
//加载的主文件
String config= "ba06/total.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(config);
//从容器中获取Student对象
Student myStudent=(Student) ac.getBean("myStudent");
System.out.println("student“"+myStudent);
}
}
基于注解的DI(主)
通过注解完成Java对象的创建,赋值。
使用步骤
1、maven中加入spring-context依赖,在加入spring-context的同时,间接加入了spring-aop的依赖。使用注解必须使用Spring-aop依赖。
2、类中加入Spring的注解。
3、在Spring的配置文件中,加入一个组件扫描器的标签,说明注解在项目中的位置。
学习的注解
1、@Component
2、@Respotory
3、@Service
4、@Controller
5、@Value
6、@Autowired
7、@Resource
创建对象
Student类
package com.daihan.ba01;
import org.springframework.stereotype.Component;
/**
* @Component:创建对象
* 属性:value 就是对象名称,也就是bean的id值
* value的值是唯一的,创建的对象在Spring容器中就一个
* 位置:类的上面
*
* @Component(value="myStudent")等同于
* <bean id="myStudent" class="com.daihan.ba01.Student"/>
*
* Spring中和@Component功能一致,创建对象的注解还有:
* @Respotory (持久层):放在dao的实现类上面,表示创建dao对象,dao对象是能访问数据库的。
* @Service (业务层):放在Service的实现类上面,创建Service对象,做业务处理
* @Controller (控制器上):放在控制器上,创建控制器对象,控制器对象能接受用户提交的参数,显示请求的处理结果
* 以上三个注解的使用语法和@Component一样。都能创建对象,但是这三个注解还有额外的功能。
* 这三个注解是给项目的对象分层的,
*
*/
//常规写法@Component(value="myStudent")
//省略value写法:
@Component("myStudent")
//不指定对象名称,由Spring提供默认名称(小写类名) 写法:@Component
public class Student {
private String name;
private Integer age;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
配置文件
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--声明组件扫描器
base-package:指定注解在项目中的包名
-->
<context:component-scan base-package="com.daihan.ba01"/>
<!--指定多个包的三种方式-->
<!--方式一:多次使用组件扫描器标签,指定不同的包-->
<!--方式二:使用分隔符(;或者,)分隔多个包-->
<context:component-scan base-package="com.daihan.ba01;com.daihan.ba02"/>
<!--方式三:指定父包-->
<context:component-scan base-package="com.daihan"/>
</beans>
主程序
package com.daihan;
import com.daihan.ba01.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class myTest {
@Test
public void test01(){
String config="applicationContext.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(config);
Student student= (Student) ac.getBean("myStudent");
System.out.println("student:"+student);
}
}
简单类型属性赋值
Student类
package com.daihan.ba02;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//省略value写法:
@Component("myStudent")
public class Student {
/**
* @Value :简单啊类型的属性赋值
* 属性:value 是String类型,表示简单类型的属性值
* 位置: 1、在属性定义上面,无需set方法,推荐使用
* 2、在set方法上面
*/
@Value(value="Kramer")
private String name;
@Value(value="22")
private Integer age;
// public void setName(String name) {
// this.name = name;
// }
//
// public void setAge(Integer age) {
// this.age = age;
// }
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
配置文件类似上一节代码
主程序代码类似上一节代码
引用类型属性赋值
byType
Student类
package com.daihan.ba03;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("myStudent")
public class Student {
@Value(value="Kramer")
private String name;
@Value(value="22")
private Integer age;
/**
* 引用类型赋值
* 1、@Autowired Spring框架提供的注解,实现引用类型赋值。
* Spring通过注解给引用类型赋值使用的是自动注入原理。@Autowired默认使用的是byType
*
* 位置:1、在属性定义的上面,无需set方法,推荐使用
* 2、在set方法上面
*/
@Autowired
private School school;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", school=" + school +
'}';
}
}
School类,省略,类似创建对象后,给简单类型赋值
配置文件同上,
主程序同上
byName
Student类
package com.daihan.ba04;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("myStudent")
public class Student {
@Value(value="Kramer")
private String name;
@Value(value="22")
private Integer age;
/**
* 引用类型赋值
* 1、@Autowired Spring框架提供的注解,实现引用类型赋值。
* Spring通过注解给引用类型赋值使用的是自动注入原理。@Autowired默认使用的是byType
*
* 位置:1、在属性定义的上面,无需set方法,推荐使用
* 2、在set方法上面
*
* 使用byName的方式:
* 1、在属性上方加入@Autowired
* 2、在属性上面加入@Qualifier(value="bean的id"):表示使用指定名称的bean完成赋值
*/
@Autowired
@Qualifier(value = "school")
private School school;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", school=" + school +
'}';
}
}
@Autowired的required属性
required属性是boolean类型,默认是true(推荐)。表示引用类型赋值失败,则程序报错,并终止执行
改写成 @Autowired(required=false)
之后,赋值失败的引用类型,会赋值为null
@Resource注解
加入依赖
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.1</version>
</dependency>
来自JDK
Student类
package com.daihan.ba06;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component("myStudent")
public class Student {
@Value(value="Kramer")
private String name;
@Value(value="22")
private Integer age;
/**
* 引用类型
* @Resource:来自JDK中的注解,Spring框架提供了对这个注解的功能支持
* 可以使用它对引用类型赋值,使用的也是自动注入,默认byName
* 位置:1、在属性定义上面,无需set方法,推荐使用
* 2、在set方法上面
*
* 默认是byName方式,先使用byName自动注入,如果失败,在使用byType
* 若只想使用byName方式,需要增加一个属性 name,name的值是bean的id
* @Resource (name="mySchool")
*/
@Resource
private School school;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", school=" + myschool +
'}';
}
}
School类
package com.daihan.ba06;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("mySchool")
public class School {
@Value("四川大学")
private String name;
@Value("成都市")
private String address;
public void setAddress(String address) {
this.address = address;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "School{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--声明组件扫描器
base-package:指定注解在项目中的包名
-->
<context:component-scan base-package="com.daihan.ba06"/>
</beans>
package com.daihan;
import com.daihan.ba06.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class myTest06 {
@Test
public void test01(){
String config="applicationContext06.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(config);
Student student= (Student) ac.getBean("myStudent");
System.out.println("student:"+student);
}
}
三、AOP面向切面编程
AOP:
面向切面编程,面向切面编程是从动态代理较低考虑程序运行过程。
AOP底层就是采用动态代理模式实现的。采用了两种:JDK动态代理(有接口),CGLIB动态代理(无接口)。
AOP就是动态代理的规范化
面向切面编程:
将交叉业务逻辑封装成切面,利用AOP容器功能将切面织入主业务逻辑中。(交叉业务逻辑是指通用的,与主业务无关的代码,例如安全检查、事务、日志等)
怎么面向切面编程:
1、分析项目功能,找出切面。
2、合理安排切面的执行时间(在目标方法前,还是后)
3、合理安排切面执行位置,在哪个方法去增加功能。
AOP的实现:
1、Spring:
Spring内部实现了aop规范,能做aop的工作。
Spring主要在事务处理时使用aop
我们项目开发时很少使用Spring的aop,因为Spring的aop很笨重
2、aspectJ:(主)
一个专门做aop的框架。Spring框架中集成了aspectJ的功能
aspectJ实现aop:
1、使用xml的配置文件,事务的时候使用
2、使用注解,项目中一般使用注解。一共用五个
什么时候使用AOP:
1、当需要给已存在的某个类增加功能,但又没有源代码的时候
2、给项目中多个类增加相同共能的时候
3、给业务方法增加例如事务、日志等功能的时候
初步使用(jdk)
动态代理:
在程序的执行过程中,创建代理对象。通过代理对象执行方法,给目标类的方法增加额外功能。(功能增强)
jdk动态代理实现步骤:
1、创建目标类,SomeServiceImpl目标类,给它的doSome,doOther方法增加输出时间和事务
2、创建InvocationHandler接口的实现类,在这个类实现给目标方法增加功能。
3、创建jdk中 类Proxy,创建代理对象,实现创建对象能力。
SomeService接口
package com.daihan.service;
public interface SomeService {
void doSome();
void doOther();
}
SomeServiceImpl目标类
package com.daihan.service.impl;
import com.daihan.service.SomeService;
//Service类代码不修改的前提下,增加其他功能
public class SomeServiceImpl implements SomeService {
@Override
public void doSome() {
System.out.println("执行业务方法doSome");
}
@Override
public void doOther() {
System.out.println("执行业务方法doOther");
}
}
要增加的方法封装进ServiceTools
package com.daihan.util;
import java.util.Date;
public class ServiceTools {
public static void doLog(){
System.out.println("执行时间:"+new Date());
}
public static void doTrans(){
System.out.println("提交执行结果");
}
}
使用jdk动态代理,创建InvocationHandler接口的实现类
package com.daihan.handler;
import com.daihan.util.ServiceTools;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
//目标对象
private Object target;//本例中,目标对象会是SomeServiceImpl类
public MyInvocationHandler(Object target) {
this.target = target;
}
//通过代理对象执行方法时,会调用执行这个invoke()
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result=null;
//doSome()执行之前加上时间
ServiceTools.doLog();
//执行目标类方法,通过Method类实现。args是所要调用方法的参数列表
result=method.invoke(target,args);//表示SomeServiceImpl.doSome()或SomeServiceImpl.doOther()
//doSome()执行之后加上提交事务
ServiceTools.doTrans();
//返回目标方法执行结果
return result;
}
}
主程序
package com.daihan;
import com.daihan.handler.MyInvocationHandler;
import com.daihan.service.SomeService;
import com.daihan.service.impl.SomeServiceImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class MyApp {
public static void main(String[] args) {
//使用jdk的Proxy创建代理对象
//1、创建目标对象
SomeService target=new SomeServiceImpl();
//2、创建InvocationHandler对象
InvocationHandler handler=new MyInvocationHandler(target);
//3、使用Proxy创建代理
SomeService proxy= (SomeService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(), handler);
//4、通过代理执行方法,会自动调用handler中的invoke()方法
proxy.doOther();
}
}
aspectJ框架使用(掌握)
实现方式:1、使用注解实现(主)。2、使用xml配置文件的标签实现(做事务时用)。
1、概念
aspect:切面,表示给业务方法增加的功能,例如日志输出,权限检查
pointcut:切入点,是一个或多个JoinPoint的集合,表示切面功能执行的位置
advice:通知,也叫增强,表示切面执行的时间,在方法前、后等等
2、切面的执行时间。这个执行时间在规范中叫做Advice(通知,增强)
在aspectJ框架中使用注解来表示的。(也可以使用xml配置文件标签)
掌握前三个通知注解
1)@Befor
2)@AfterReturning
3)@Around
4)@AfterThrowing
5)@After
通知注解修饰的方法叫做通知方法。被增强功能的叫目标方法。
3、切面执行的位置。使用的切入点表达式:
execution(modifiers-pattern?
ret-type-pattern
declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
解释:
modifiers-pattern: 访问权限类型
ret-type-pattern 返回值类型
declaring-type-pattern 包名类名
name-pattern(param-pattern) 方法名(参数类型和参数个数)
throws-pattern 抛出异常类型
? 可选的部分
以上表达式共四个部分:
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
**包名类名+方法名算一个部分**
方法返回值 方法声明(参数)是必须的
四个部分之间用空格进行分隔。
有pattern 可以使用通配符:
*
:表示0至任意多字符
…:用在方法参数中表示任意多个参数;用在包名后,表示当前包及其子包路径
例如:execution(public * *(...))
指定切入点为任意公共方法
execution(* set*(...))
指定切入点为任意以set开头的方法
@Before
在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个JoinPoint类型参数。该类型的对象本身就是切入点表达式。通过该参数,可以获取切入点表达式、方法签名、目标对象等。
前置操作、加入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
第一步,创建业务接口及其实现类
package com.daihan.ba01;
public interface SomeService {
public void doSome(String name,Integer age);
}
package com.daihan.ba01;
public class SomeServiceImpl implements SomeService {
@Override
public void doSome(String name,Integer age) {
//给doSome方法增加一个功能,在doSome方法执行前,输出方法执行时间
System.out.println("=======doSome()执行=========");
}
}
第二步、创建切面类
package com.daihan.ba01;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import java.util.Date;
/**
* @Aspect 是aspectJ中的注解
* 作用:表示当前类是切面类
* 切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
* 位置:在类定义的上面
*/
@Aspect
public class MyAspect {
/**
* 定义方法,方法是实现切面功能
* 方法定义要求:
* 1、公共的方法public
* 2、方法没有返回值
* 3、方法名称自定义
* 4、方法可以有参数,也可以没有参数
* 如果有参数,参数不是自定义的,有几个参数类型可以使用
*/
/**
* @Before 前置通知注解
* 属性:value,是切入点表达式,表示切入点执行功能的位置
* 位置:在方法上面
* 特点:1、在目标方法之前执行
* 2、不会改变目标方法执行结果
* 3、不会影响目标方法的执行。
*
*/
@Before(value="execution(public void *..SomeServiceImpl.doSome(String ,Integer ))")
public void myBefore(){
//切面要执行的功能代码
System.out.println("前置通知,在目标方法执行前输出执行时间:"+new Date());
}
}
第三步、创建Spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--把对象交给Spring容器,由Spring容器同一创建-->
<!--声明目标对象-->
<bean id="someService" class="com.daihan.ba01.SomeServiceImpl"/>
<!--声明切面类对象-->
<bean id="myAspect" class="com.daihan.ba01.MyAspect"/>
<!--声明自动代理生成器:使用aspectj框架内部功能,创建目标代理的代理对象
创建代理对象实在内存中实现的,修改目标对象的内存中的结构。将其创建为代理对象
所以目标对象就是被修改后的代理对象
aspectj-autoproxy:会把Spring容器中的所有目标对象,一次性生成代理对象
-->
<aop:aspectj-autoproxy/>
</beans>
第四步、测试
package com.daihan;
import com.daihan.ba01.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest01 {
@Test
public void test01(){
String config="applicationContext.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(config);
//从容器中获取目标对象
SomeService proxy = (SomeService) context.getBean("someService");
//通过代理对象执行方法,实现目标方法执行时增强功能
proxy.doSome("lisi",20);
}
}
插入知识点JoinPoint
指定通知方法中的参数:JoinPoint
JoinPoint:要加入切面功能的业务方法
作用:可以在通知方法(通知注解例如@Before修饰的方法)中获取方法执行时的信息,例如方法名称,方法实参
JoinPoint参数指是由框架赋予的,**必须是第一个参数位置**
原有切面类,根据业务稍加修改
@Before(value="execution(public void *..SomeServiceImpl.doSome(String ,Integer ))")
public void myBefore(JoinPoint jp){
//获取方法的完整定义
System.out.println("方法的签命(定义):"+jp.getSignature());
//获取方法名
System.out.println("方法的名称:"+jp.getSignature().getName());
//获取方法的参数
Object args[]=jp.getArgs();
for (Object arg:args){
System.out.println("参数="+arg);
}
//切面要执行的功能代码
System.out.println("前置通知,在目标方法执行前输出执行时间:"+new Date());
}
@AfterReturing
第一步,创建业务接口及其实现类
package com.daihan.ba02;
public interface SomeService {
public void doSome(String name,Integer age);
public String doOther(String name,Integer age);
}
package com.daihan.ba02;
public class SomeServiceImpl implements SomeService {
@Override
public void doSome(String name,Integer age) {
//给doSome方法增加一个功能,在doSome方法执行前,输出方法执行时间
System.out.println("=======doSome()执行=========");
}
@Override
public String doOther(String name, Integer age) {
System.out.println("=======doOther()执行=========");
return "abcd";
//在代码执行后,执行业务,例如事务提交
}
}
第二步、创建切面类
package com.daihan.ba01;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import java.util.Date;
@Aspect
public class MyAspect {
/**
* 后置通知定义方法,方法是实现切面功能
* 方法定义要求:
* 1、公共的方法public
* 2、方法没有返回值
* 3、方法名称自定义
* 4、方法有参数,参数推荐是Object,参数名自定义
*/
/**
* @AfterReturning :后置通知
* 属性:1、value 切入点表达式
* 2、returning 自定义变量,表示目标方法返回值
* 自定义变量名必须和通知方法的形参名一样
* 位置:在方法定义的上面
* 特点:1、在目标方法执行之后执行
* 2、能获取目标方法的返回值,可以根据这个返回值做不同的处理功能。
* 即 本例Object res=doOther(),通知方法就可以使用res了
* 3、可以修改返回值
*
* 后置通知的执行
* 先Objext res=doOther();
* 再myAfterReturning(res);
*/
@AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",returning = "res")
public void myAfterReturning(Object res){
//Object res就是目标方法执行后的返回值,根据返回值做切面的功能
System.out.println("后置通知:在目标方法之后执行"+res);
//后置通知方法不会改变String
if(res!=null)res="111";
}
}
疑问:后置通知方法是否能改变引用类型例如自定义Student类对象的属性值
第三步、创建Spring配置文件
同上
第四步、测试
package com.daihan;
import com.daihan.ba02.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest02 {
@Test
public void test01(){
String config="applicationContext02.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(config);
//从容器中获取目标对象
SomeService proxy = (SomeService) context.getBean("someService");
//通过代理对象执行方法,实现目标方法执行时增强功能
String str=proxy.doOther("张三",20);
System.out.println(str);
}
}
@Around
第一步,创建业务接口及其实现类
package com.daihan.ba03;
public interface SomeService {
public void doSome(String name,Integer age);
public String doOther(String name,Integer age);
public String doFirst(String name,Integer age);
}
package com.daihan.ba03;
public class SomeServiceImpl implements SomeService {
@Override
public void doSome(String name,Integer age) {
//给doSome方法增加一个功能,在doSome方法执行前,输出方法执行时间
System.out.println("=======doSome()执行=========");
}
@Override
public String doOther(String name, Integer age) {
System.out.println("=======doOther()执行=========");
return "abcd";
//在代码执行后,执行业务,例如事务提交
}
@Override
public String doFirst(String name, Integer age) {
System.out.println("=======业务方法doFirst======");
return "doFirst";
}
}
第二步、创建切面类
package com.daihan.ba03;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import java.util.Date;
/**
* @Aspect 是aspectJ中的注解
* 作用:表示当前类是切面类
* 切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
* 位置:在类定义的上面
*/
@Aspect
public class MyAspect {
/**
* 环绕通知方法定义格式
* 1、public
* 2、必须有一个返回值,推荐使用Object
* 3、方法名称自定义
* 4、方法有固定参数 ProceedingJoinPoint
*/
/**
* @Around 环绕通知
* 属性:value 切入点表达式
* 位置:方法定义上面
* 特点:功能最强的通知
* 在目标方法前后都能增加功能
* 控制目标方法是否被调用执行
* 修改原来目标方法的执行结果,影响最后的调用结果
* 环绕通知等同于jdk动态代理的InvocationHandler接口
*
* 参数:ProceedingJoinPoint等同于jdk动态代理中的Method
* 作用是执行目标方法
* 返回值:目标方法的执行结果,可以被修改
*/
@Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
String name="";
//获取第一个参数值
Object args[]=pjp.getArgs();
if(args!=null&&args.length>1){
Object arg=args[0];
name=(String)arg;
}
//实现环绕通知
Object result=null;
System.out.println("在目标方法执行前输出时间:"+new Date());
//1、目标方法调用
if("张三".equals(name)){
//符合条件 调用方法
result=pjp.proceed();//等同于jdk中method。invoke();
}
//result=pjp.proceed();//等同于jdk中method。invoke();
System.out.println("在目标方法执行后提交事务");
//2、在目标方法前/后加功能
//加在目标方法前,就写在result=pjp.proceed();上面
//返回目标方法执行结果
return result;
}
}
第三步、创建Spring配置文件
同上
第四步、测试
package com.daihan;
import com.daihan.ba03.SomeService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest03 {
@Test
public void test01(){
String config="applicationContext03.xml";
ApplicationContext context = new ClassPathXmlApplicationContext(config);
//从容器中获取目标对象
SomeService proxy = (SomeService) context.getBean("someService");
//通过代理对象执行方法,实现目标方法执行时增强功能
String str=proxy.doFirst("张三1",20);
//System.out.println(str);
}
}
@Around可以修改目标方法执行结果,影响方法最后的调用结果
切面类:
package com.daihan.ba03;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import java.util.Date;
/**
* @Aspect 是aspectJ中的注解
* 作用:表示当前类是切面类
* 切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
* 位置:在类定义的上面
*/
@Aspect
public class MyAspect {
/**
* 环绕通知方法定义格式
* 1、public
* 2、必须有一个返回值,推荐使用Object
* 3、方法名称自定义
* 4、方法有固定参数 ProceedingJoinPoint
*/
/**
* @Around 环绕通知
* 属性:value 切入点表达式
* 位置:方法定义上面
* 特点:功能最强的通知
* 在目标方法前后都能增加功能
* 控制目标方法是否被调用执行
* 修改原来目标方法的执行结果,影响最后的调用结果
* 环绕通知等同于jdk动态代理的InvocationHandler接口
*
* 参数:ProceedingJoinPoint等同于jdk动态代理中的Method
* 作用是执行目标方法
* 返回值:目标方法的执行结果,可以被修改
*
* 环绕通知用途:经常用于事务,在目标方法之气那开启事务,执行目标方法,之后提交事务
*/
@Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
String name="";
//获取第一个参数值
Object args[]=pjp.getArgs();
if(args!=null&&args.length>1){
Object arg=args[0];
name=(String)arg;
}
//实现环绕通知
Object result=null;
System.out.println("在目标方法执行前输出时间:"+new Date());
//1、控制目标方法调用,满足条件时才调用
if("张三".equals(name)){
//符合条件 调用方法
result=pjp.proceed();//等同于jdk中method。invoke();
}
//result=pjp.proceed();//等同于jdk中method。invoke();
System.out.println("在目标方法执行后提交事务");
//2、在目标方法前/后加功能
//注:加在目标方法前,就写在result=pjp.proceed();上面
//修改目标方法的执行结果,影响方法最后的调用结果
if(result!=null){
result="hello AspectJ AOP";
}
//返回目标方法执行结果
return result;
}
}
@AfterThrowing
1、在目标方法抛出异常时执行
2、可以做异常的监控程序
有两个属性:
value:切入点表达式
throwing:切面方法参数public void myAfterThrowing(Exception ex){...}
@After
1、最终通知,总是会执行的,在目标方法之后执行
2、public、没有返回值、方法名自定义、没有参数 有的话就是JoinPoint
属性:
value:切入点表达式
@Pointcut
不是通知注解,是一个辅助注解。
Pointcut 定义和管理切入点,如果项目中有多个切入点表达式是重复的,可以复用的,则可以使用Pointcut。
属性(value=切入点表达式)
位置 自定义方法上面
特点:当使用@Pointcut在方法上面时,这个方法的名称就是切入点表达式的别名。其他通知中value属性就可以直接使用这个方法的名称代替切入点表达式。方法内无需代码。
CGLIB
上述代码有接口的时候,底层使用的就是jdk动态代理方式。(有接口也能用CGLIB动态代理,配置文件 <aop:aspectj-autoproxy/>
改为 <aop:aspectj-autoproxy proxy-target-class="true"/>
)
上述代码没有接口的时候,底层使用的就是CGLIB动态代理。
四、Spring集成MyBatis
把Mybatis框架和Spring框架集成在一起,像一个框架一样使用。使用的技术是ioc。为什么ioc能集成Mybatis和Spring呢?是因为ioc能创建对象,可以把Mybatis中的对象交给Spring统一创建,开发人员从Spring中获取对象。
复习Mybatis使用步骤
1、定义dao接口,StudentDao
2、定义mapper文件,StudentDao.xml
3、定义mybatis主配置文件
4、创建dao代理对象
StudentDao dao=SqlSession.getMapper(StudentDao.class);
List<Student> students=dao.selectStudents();
要使用dao对象,需要getMapper()方法,使用getMapper()方法,需要条件:
1、获取SqlSession对象,需要SqlSessionFactory的openSession()方法。
2、创建SqlSessionFactory对象。通过读取Mybatis主配置文件,能创建SqlSessionFactory对象。
总之,需要SqlSessionFactory对象,使用Factory能获取SqlSession对象,有了SqlSession对象就能有dao,目的就是获取dao对象。
通过以上说明,我们需要让Spring创建一下对象
1、独立的连接池类对象,使用阿里的druid连接池。
2、SqlSessionFactory对象
3、创建dao对象
需要学习的就是上面三个对象的创建语法。使用xml的bean标签
具体实现
步骤:
1、创建maven项目
2、加入依赖
1)Spring依赖
2)Mybatis依赖
3)mysql驱动
4)Spring事务的依赖
5)Mybatis和Spring集成的依赖
3、创建实体类
4、创建dao接口和mapper文件
5、创建Mybatis主配置文件
6、创建Setvice接口和实现类,属性是dao
7、创建Spring的配置文件,声明Mybatis的对象交给Spring创建
1)数据源:DataSource
2)SqlSessionFactory
3)Dao对象
4)声明自定义Service
8、创建测试类
maven依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--Spring核心ioc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--做Spring事务-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--Mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!--Spring、Mybatis集成的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!--Mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>
<!--阿里公司的数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
</dependencies>
<build>
<!--目的是把src/main/java目录中的xml文件包含到输出结果中。输出的classes目录中-->
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,.xml文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<!--指定jdk版本-->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
dao包:
接口
package com.daihan.dao;
import com.daihan.domain.Student;
import java.util.List;
public interface StudentDao {
int insertStudent(Student student);
List<Student> selectStudents();
}
xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.daihan.dao.StudentDao">
<insert id="insertStudent" >
insert into student value(#{id},#{name},#{email},#{age})
</insert>
<select id="selectStudents" resultType="com.daihan.domain.Student">
select id,name,email,age from student order by id desc
</select>
</mapper>
实体包
Student
service包
StudentService 接口
package com.daihan.servicce;
import com.daihan.domain.Student;
import java.util.List;
public interface StudentService {
int addStudent(Student student);
List<Student> queryStudent();
}
StudentService 实现类
package com.daihan.servicce;
import com.daihan.dao.StudentDao;
import com.daihan.domain.Student;
import java.util.List;
public class StudentServiceImpl implements StudentService {
//引用类型dao
private StudentDao studentDao;
//使用set注入来赋值
public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}
@Override
public int addStudent(Student student) {
int i = studentDao.insertStudent(student);
return i;
}
@Override
public List<Student> queryStudent() {
List<Student> students=studentDao.selectStudents();
return students;
}
}
main/resources目录下
Spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
<!--声明数据源DataSource,作用是连接数据库的-->
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<!--set注入给DruidDataSource提供连接数据库的信息-->
<property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8"/><!--setUrl()-->
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
<!--声明mybatis中的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory的-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--set注入,把数据库连接池付给dataSource属性-->
<property name="dataSource" ref="myDataSource"/>
<!--mybatis主配置文件的位置
configLocation属性是Resource类型的,读取配置文件
它的赋值使用的是value,指定文件路径,使用classpath:指定文件位置
-->
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
MapperScannerConfigurer在内部调用getMapper()生成每个dao接口的代理对象
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定SqlSessionFactory对象的id-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--指定包名,包名是dao接口所在的包名
MapperScannerConfigurer会扫描这个包中所有接口
把每个接口都执行一次getMapper()方法,得到每个接口的dao对象。
创建好的dao对象放入Spring的容器中。dao对象的默认名称是 接口名首字母小写
-->
<property name="basePackage" value="com.daihan.dao"/>
</bean>
</beans>
数据库配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!--设置mybatis打印日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--设置别名-->
<typeAliases>
<package name="com.daihan.domain"/>
</typeAliases>
<mappers>
<!--<mapper resource="com/daihan/dao/StudentDao.xml"/>-->
<!--
name :是包名.
这个包下所有xml一次性找到
-->
<package name="com.daihan.dao"/>
</mappers>
</configuration>
五、Spring事务
概念
一、什么是事务
事务是指一组sql语句的聚合,我们希望这些sql语句能整体成功或整体失败。
二、什么时候想到使用事务
涉及多个表,或者多个sql语句的insert,update,deletede。需要保证这些语句全都成功或失败。
Java代码中写程序,控制事务,此时事务应该放在那里?放在service类的业务方法上,业务方法调用多个dao方法执行sql语句
三、通常使用JDBC访问数据库,还是Mybatis访问数据库,怎么处理事务
JDBC访问数据库,处理事务 Connection conn; conn.commit() ; conn.rollback();
mybatis访问数据库,处理事务,SqlSession.commit();SqlSession.rollback();
四、3问题中事务处理方式有什么不足
1)不同数据库访问技术,处理事务对象,方法不同,需要了解不同数据库访问技术使用事务的原理
2)需要掌握多种数据库事务的处理逻辑。什么时候提交事务,什么时候回滚事务。
3)需要掌握处理事务的多种方法
总结:就是多种数据库的访问技术,有不同的事务处理机制,对象,方法。
五、怎么解决不足
Spring提供了一种处理事务的统一模型,能使用一种统一的步骤,方式完成多种不同数据库访问技术的事务处理。
使用Spring事务处理机制,可以完成Mybatis、Hibernate等的访问数据库事务处理。
六、处理事务,需要怎么做,做什么
Spring处理事务的模型,使用步骤是固定的,把事务使用的信息提供给Spring就行了。
1、事务内部提交,回滚事务,使用的事务管理器对象,代替程序员完成commit,rolback
事务管理器是一个接口和他的众多实现类。
接口:PlatformTransactionManager,定义了事务重要方法commit,rollback
实现类:Spring把每一种数据库访问技术对应的事务处理类都创建好了。
使用:需要告诉Spring使用的是哪种数据库访问技术,通过声明数据库访问技术对应的事务管理器实现类。
例如:使用Mybatis访问数据库则声明 <bean id=" xxx" class="...DataSourceTransactionManager">
2、业务方法需要做什么样的事务,需要说明事务的类型。
说明方法需要的事务:
1)事务的隔离级别:实际有4个值。
ISOLATION_DEFAULT :Mysql默认ISOLATION_REPEATABLE_READ
ISOLATION_READ_UNCONMITTED:读未提交
ISOLATION_READ_COMMITED:读已提交
ISOLATION_REPEATABLE_READ:可以重复读
ISOLATION_SERIALIZABLE:串行化
2)事务的超时时间:
表示一个方法最长执行时间,超出时间,事务回滚。单位是秒,整数值,默认-1
3)事务的传播行为:
控制业务方法是不是有事务的,是什么样的事务
7个传播行为,表示业务方法调用时,事务在方法之间是如何使用的。掌握三个
1)PROPAGATION_REQUIRED(默认):方法必须在事务内执行。事务开始,执行方法,事务结束。
2)PROPAGATION_REQUIRES_NEW:总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务完成
3)PROPAGATION_SUPPORTS:指定方法支持当前事务,但若当前没有事务,也可以以非事务方法执行
4)PROPAGATION_MANDATORY
5)PROPAGATION_NESTED
6)PROPAGATION_NEVER
7)PROPAGATION_MOT_SUPPORTED
3、Spring提交事务,回滚事务的时机
1)当业务方法执行成功,没有异常抛出,当方法执行完毕。Spring在方法执行后提交事务。调用事务管理器的commit
2)业务方法抛出运行时异常,Spring执行回滚。调用事务管理器的rollback
3)非运行时异常,主要是受查异常时,提交事务。
总结Spring事务
1、管理事务的是 事务管理和他的实现类
2、Spring的事务是一个统一模型
1)指定方法需要的隔离级别,传播行为,超时
Spring框架中提供的事务处理方案
1、注解方案(中小项目)
Spring自己使用AOP实习那给业务方法增加事务功能,使用@Transactional注解增加事务。
@Transactional注解是Spring框架自己的注解,放在public方法上面,表示当前方法具有事务。
可以给注解属性赋值,表示具体隔离级别,传播行为,异常信息等。
属性:
propagation:(枚举)设置事务传播行为
isolation:(枚举)设置事务隔离级别
readOnly:(布尔)设置方法对数据库操作是否是只读。
timeout:(int)超时时间。一般不设置
rollbackFor:(Class[])指定要回滚的异常类
使用步骤
1、声明事务管理器对象 <bean id="xx" class="DataSourceTransactionManager">
2、开启事务注解驱动,告诉Spring要使用注解的方式管理事务
Spring使用aop机制,创建@Transactional所在的类代理对象,给方法加入事务功能。
Spring给业务方法加入事务:
在业务方法执行之前,开启事务,在业务方法之后提交或回滚事务,使用aop环绕通知。
3、在方法上面加注解
实现
做事务的环境项目,步骤
1、创建maven项目
2、加入依赖
1)Spring依赖
2)Mybatis依赖
3)mysql驱动
4)Spring事务的依赖
5)Mybatis和Spring集成的依赖
3、创建实体类
Good,Sale
4、创建dao接口和mapper文件
SaleDao接口,GoodDao接口
SaleDao.xml,GoodDao.xml
5、创建Mybatis主配置文件
6、创建Setvice接口和实现类,属性是dao
7、创建Spring的配置文件,声明Mybatis的对象交给Spring创建
1)数据源:DataSource
2)SqlSessionFactory
3)Dao对象
4)声明自定义Service
8、创建测试类
无事务情况:D:\JAVACODE\Spring\ch08-Spring-trans
增加事务具体实现: D:\JAVACODE\Spring\ch09-Spring-trans -anno
2、AspectJ框架功能(大型项目)
大型项目有很多类,方法,需要大量的配置事务,使用AspectJ框架功能,在Spring配置文件中声明类,方法需要的事务。这种方法下 业务方法和事务配置完全分离。
步骤:在配置文件实现
1、要使用AspectJ框架,需要加入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
2、声明事务管理器对象 <bean id="xx" class="DataSourceTransactionManager">
3、声明方法需要的事务类型(配置方法的事务属性(隔离级别,传播,超时))
4、配置aop:指定哪些类要创建代理
具体 D:\JAVACODE\Spring\ch10-Spring-trans-aspectj
六、Spring与Web
web项目是在Tomcat服务器上运行的,Tomcat一启动,项目一直运行的。
在我们的web项目中容器对象只需要创建一次,把这个容器对象放入全局作用域ServletContext中。
实现:
使用监听器,当全局作用域对象被创建时 创建容器,存入ServletContext。
监听器作用:
1、创建容器对象,执行
String config="applicationContext.xml"; ApplicationContext ac=new ClassPathXmlApplicationContext(config);
2、把容器对象放入ServletContextServletContext.setAttribute(key,ac)
监听器可以自己创建,也可以使用框架提供好的ContextLoaderListener。
private WebApplicationContext context;
public interface WebApplicationContext extends ApplicationContext
ApplicationContext:javaSE项目中使用的容器
WebApplicationContext :web项目中使用的容器
具体 D:\JAVACODE\Spring\ch11-Spring-web