超详细!!Spring5框架开发笔记
Spring5开发教程
简介
spring特性
sping是轻量级的开源的JavaEE框架
Spring可以解决企业应用开发的复杂性
Sping两个核心的部分:IOC和AOC
IOC:控制反转。把创建对象的过程交给sping进行管理,而不需要自己去new
AOP:面向切面。不修改源代码进行功能增强
Sping特点
方便接耦,简化开发。
AOP编程支持
方便程序的测试
方便集成各种优秀框架
方便进行事务操作
降低API开发难度
简单使用
如果使用maven构建项目,导入spring核心依赖包就可以了:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
随便创建一个User类,然后创建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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置User对象创建-->
<bean id="user" class="com.atguigu.spring5.User"></bean>
</beans>
class是类路径
编写测试代码
public class TestSpring5 {
@Test
public void testAdd(){
//1 加载spring配置文件
ApplicationContext context = new FileSystemXmlApplicationContext("/src/main/java/bean1.xml"); //方式1
//BeanFactory context = new FileSystemXmlApplicationContext("/src/main/java/bean1.xml");//方式2
//2 获取配置创建的对象
User user = context.getBean("user",User.class);
System.out.println(user);
user.add();
}
}
此时,配置文件已经帮我们创建好了实例,我们直接从配置文件中获取就行了。
Spring IOC容器
IOC底层原理
什么是IOC
Inversion Of Control(控制反转)。把对象创建和对象之间的调用过程,交给Spring管理;使用IOC的目的是为了降低耦合度。
IOC底层主要用到三个技术:xml解析、工厂模式、反射。
工厂模式其实就把把对象的创建交给第三方,降低耦合度(完全没有耦合度是不能的)
class UserService{
execute(){
UserDao dao = new UserFactory.getDao();
}
} class UserDao{
...
} class UserFactory{
public static UserDao getDao(){
return new UserDao();
}
}
反射就是java代码可以动态获取类的字节码文件,从而可以得到类的属性和方法等。
IOC过程
xml配置文件,配置创建的对象
有service类,dao类,创建工厂类
class UserFactory{
public static UserDao getDao(){
String classValue = class属性值 //1 xml解析
Class class = Class.forName(classValue);//2 通过反射创建对象
return (UserDao) class.newInstance();
}
}
IOC接口
IOC思想基于IOC容器完成,IOC容器底层就是对象工厂。
Spring提供IOC容器实现两种方式:(两个接口)
BeanFactory:IOC容器最基本实现,是Spring内部的使用接口,不提供开发人员进行使用
*加载配置文件的时候不会创建对象,在获取对象(getBean)才去擦黄建对象
ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员使用
*加载配置文件配置文件的时候就会把配置文件对象进行创建
一般把耗时耗资源的过程交给服务器启动的时候去完成,所以ApplicationContext更合适。
IOC操作Bean管理——XML方式
Bean管理是Spring的两个操作:Spring创建对象;Spring注入属性(不需要写set方法,这里的属性可以是基本类型,也可以是类对象)。
Bean管理操作有两种方式:基于xml配置文件方式实现;基于注解方式实现。
基于xml方式
创建对象
<bean id="user" class="com.atguigu.spring5.User"></bean>
<!--
----------
bean标签常用的属性
*id:唯一标识
*class:类全路径(包类路径)
*name:作用和id一样,可以加特殊符号,目前已弃用
----------
创建对象时候,默认也是执行无参构造方法
----------
-->
注入属性—基本数据类型
DI:依赖注入,就是注入属性。
<bean id="user" class="com.atguigu.spring5.User">
<!-- set方法注入属性:使用property完成属性注入 -->
<property name="userName" value="xing"></property>
<!--
有参构造方法注入属性
<constructor-arg name="userName" value="xing"></constructor-arg>
-->
</bean>
使用p名称空间注入,可简化基于xml配置方式
<!--xmlns:p="http://www.springframework.org/schema/p"-->
<bean id="user" class="com.atguigu.spring5.User" p:userName="xing"></bean>
xml注入其他属性
<!-- 设置空值 -->
<property name="userName">
<null></null>
</property> <!-- 特殊符号如:<<>> -->
<property name="userName">
<value><![CDATA[<<xing>>]]></value>
</property>
注入属性—外部bean
创建一个service和dao对象,其中service里面有一个dao作为属性
public class UserService {
//创建UserDao类型属性,生成set方法
private UserDao userDao; public void setUserDao(UserDao userDao) {
this.userDao = userDao;
} public void add(){
userDao.update();
}
}
配置文件
<!-- service和dao对象的创建-->
<bean id="userService" class="com.atguigu.spring5.service.UserService">
<!-- 注入userDao对象
name属性值:类里面属性名称
ref属性:创建userDao对象bean标签id值
-->
<!-- set 方法注入属性-->
<property name="userDao" ref="userDaoImpl"></property>
</bean>
<!-- userDao的创建class要指向实现类-->
<bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl"></bean>
注入属性—内部bean
内部bean可以理解在一个bean里面嵌套定义另外一个bean,实际应用中,更常用外部bean,因为更加清晰。
<bean id="userService" class="com.atguigu.spring5.service.UserService">
<property name="userDao">
<bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl">
</property>
</bean>
注入属性—级联赋值
级联赋值就是注入外部bean的时候,在外部bean里面赋值,比如:
<bean id="userService" class="com.atguigu.spring5.service.UserService">
<property name="userDao" ref="userDaoImpl"></property>
</bean> <bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl">
<property name="userDaoProperty" value="value"></property>
</bean>
还有一种方式:这种方式要在userService中设置userDaoImpl的set方法,因为userDaoImpl.userDaoProperty要通过set方法来获取
<bean id="userService" class="com.atguigu.spring5.service.UserService">
<property name="userDao" ref="userDaoImpl"></property>
<property name="userDaoImpl.userDaoProperty" value="value2"></property>
</bean>
注入属性—集合属性
public class Stu {
private String[] courses;
private List<String> list;
private Map<String,String> map; //对象list
private List<Course> courseList;
public void setCourses(String[] courses) {
this.courses = courses;
} public void setList(List<String> list) {
this.list = list;
} public void setMap(Map<String, String> map) {
this.map = map;
} public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}
}
<bean id="stu" class="com.atguigu.spring5.collection.Stu">
<!-- 数组属性的注入 -->
<property name="courses">
<array>
<value>java课程</value>
<value>数据库标签</value>
</array>
</property>
<!-- list类型属性注入 -->
<property name="list">
<list>
<value>张三</value>
<value>李四</value>
</list>
</property>
<!-- map类型属性注入 -->
<property name="map">
<map>
<entry key="Java" value="java"></entry>
</map>
</property>
<!-- 注入对象list类型 -->
<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
</bean>
<bean id="course1" class="com.atguigu.spring5.collection.Course">
<property name="cname" value="Spring5框架"></property>
</bean>
<bean id="course2" class="com.atguigu.spring5.collection.Course">
<property name="cname" value="Mybatis框架"></property>
</bean>
还可以把集合注入部分提取出来,方法是在spring配置文件中引入新的名称空间比如util,模版如下:
<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 http://www.springframework.org/schema/utils/spring-utils.xsd"> <!-- 提取list集合类型属性注入 -->
<util:list id="bookList">
<value>java</value>
<value>c++</value>
</util:list> <bean id="book" class="com.atguigu.spring5.collection.Book">
<property name="list" ref="bookList"></property>
</bean>
</beans>
FactoryBean
Spring有两种类型的bean,一种普通bean,另一种工厂bean(FactoryBean)。
普通bean在Spring配置文件中定义什么类型,返回就是什么类型;而工厂bean定义的类型和返回的类型可以不一致。
做法:
创建类,让这个类作为工厂bean,实现接口FactoryBean
实现接口里面的方法,在实现的方法中定义返回的bean类型
public class MyBean implements FactoryBean<Course> {
//定义返回bean
public Course getObject() throws Exception {
Course course = new Course();
course.setCname("java");
return course;
} public Class<?> getObjectType() {
return null;
}
}
<bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean"></bean>
public class TestSpring5 {
ApplicationContext context = new FileSystemXmlApplicationContext("/src/main/java/bean1.xml");
Course course = context.getBean("myBean",Course.class);
}
Bean的作用域
bean的作用域是指:在Spring里面,设置创建bean实例是单实例还是多实例(每次getBean都获取新对象),默认情况下是单实例对象。
在bean标签里面scope
属性用来设置单实例还是多实例,默认值singleton
,prototype
表示多实例。
两个值的区别:singleton时候,加载spring配置文件时候就会创建单实例对象,而prototype在调用getBean方法时候才会去创建。
Bean的生命周期
- 通过构造器创建bean实例(无参数构造)
- 为bean的属性设置值和对其他bean引用(调用set方法)
- *把bean实例传递bean后置处理器(实现BeanPostProcessor的类对象)的方法
- 调用bean的初始化方法(需要配置初始化的方法
bean标签的init-method属性
) - *把bean实例传递bean后置处理器的方法
- bean可以使用了(对象获取到了)
- 当容器关闭的时候(context.close()),调用bean的销毁方法(需要配置销毁的方法
bean标签的destroy-method属性
)
xml自动装配
根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入
<!--
bean标签属性autowire,配置自动装配
autowire属性常用两个值:
byName:根据属性名称注入,注入bean的id值和类属性名称一样
byType:根据属性类型注入,相同类型的bean不能有多个
-->
<bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byName"></bean>
<bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>
基于xml的自动装配很少用到,基本都是使用注解
引入外部属性文件
以配置数据库连接池为例:
创建外部属性文件.properties格式文件,写数据库信息
prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.userName=root
prop.passWord=root
#等号左边可以随便写,但建议不要写某一个单词,容易冲突
把外部properties属性文件引入到spring配置文件中
首先引入context名称空间,先后在spring配置文件中使用标签引入外部属性文件
<!-- 引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--配置连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass}"></property>
...
</bean>
IOC操作Bean管理——注解方式
注解可以作用在类、属性、方法上面,可以简化我们的xml配置。
Spring针对Bean管理中创建对象提供的注解四种,他们都作用在类上面:
- @Component
- @Service
- @Controller
- @Repository
*** 这四个注解功能是一样的,都可以用来创建bean实例,只不过在开发中为了清晰的表明这个bean用在哪一个层上面**
使用注解创建对象
引入AOP依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
开启组件扫描
指定要扫描的包,这个包中的类有注解就回去创建对象。
首先引入context名称空间
<!--开启组件扫描
1 如果扫描多个包,多个包使用逗号隔开
2 也可以扫描包的上层目录(推荐)
-->
<context:component-scan base-package="com.atguigu.spring5"></context:component-scan>
创建类,在类上面添加创建对象注解即可
//value属性可以省略不写,默认值是类名称首字母小写
@Component(value = "userService") //<bean id="userService" class="..."></bean>
public class UserService{
//...
}
开启组件扫描细节配置
这个表示只扫描包里面带Controller注解的类
<!--示例1
use-default-filters="false" 表示现在不使用默认的filter(所有类都扫描),自己配置filter
context:include-filter,设置扫描哪些内容
-->
<context:component-scan base-package="com.atguigu.spring5" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--示例2
context:exclude-filters:设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.atguigu.spring5">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
基于注解方式实现属性注入
@AutoWired:根据属性类型进行自动装配(byType),不需要添加set方法,如果接口有多个实现类的话,@AutoWired就不知道找哪个了,这个时候就要配合@Qualifier根据名称进行自动装配了
@Qualifier:根据属性名称进行自动装配(byName),
要和@AutoWired一起使用
@AutoWired
@Qualifier(value = "userDaoImpl")
private UserDao userDao;
@Resource:可以根据类型注入,也可以根据名称注入。这个注解在javax.annotation.Resource包中,不是Spring本身提供的,因此官方建议使用前两者。
@Value:注入普通类型属性
@Value(value="abc")
private String name;
完全注解开发
创建配置类,替代xml配置文件
@Configuration //Spring才能识别为配置类,替代xml文件
@ComponentScan(basePackages = {"com.atguigu.spring5"})
public class SpringConfig { }
加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
Spring AOP
概念
AOP:Aspect Oriented Programming,面向切面编程。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
通俗点说就是:在不修改源代码的情况下增强类的功能。
底层原理
AOP底层使用动态代理。有两种情况动态代理:
- 有接口情况:使用JDK动态代理,创建接口实现类代理对象来增强接口功能
- 和没有接口情况:使用CGLIB动态代理,创建当前类子类的代理对象来增强类功能
使用JDK动态代理:
使用java.lang.reflect.Proxy类里面的newProxyInstance方法创建代理对象,首先我们创建接口以及实现类
static Object newProxyInstance(ClassLoader loader,class<?>[] interfaces,InvocationHandler h);
//返回指定接口的代理类实现,该接口将方法调用分派给指定的调用处理程序
//第一个参数:类加载器
//第二个参数:增强类所实现的接口,可以有多个
//第三个参数:实现InvocationHandler接口的一个对象,这个对象里面通过实现invoke()方法来写增强的逻辑
public interface UserDao {
public int add(int a,int b);
public String update(String id);
} //-------
public class UserDaoImpl implements UserDao{
public int add(int a, int b) {
System.out.println("add方法执行了。。。");
return a+b;
} public String update(String id) {
System.out.println("update方法执行了。。。");
return id;
}
}
使用Proxy类创建接口实现类代理对象
public class JDKProxy {
public static void main(String[] args) {
//创建接口实现类代理对象
Class[] interfaces = {UserDao.class};
// Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// return null;
// }
// });
UserDaoImpl userDao = new UserDaoImpl();
UserDao dao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,new UserDaoProxy(userDao));
int result = dao.add(1,2);
System.out.println("result:"+result);
}
} class UserDaoProxy implements InvocationHandler{
//1 创建的是谁的代理对象,就把谁传进来
//有参构造传递
private Object obj;
public UserDaoProxy(Object obj){
this.obj = obj;
} //写增强逻辑
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法之前
System.out.println("方法之前执行。。。"+method.getName()+"传递的参数:"+ Arrays.toString(args)); //被增强的方法执行
//这里也可以判断执行的方法去做不同的处理
Object res = method.invoke(obj,args); //方法执行之后
System.out.println("方法之后执行..."+obj);
return null;
}
}
AOP术语
- 连接点:在一个类中,哪些方法可以被增强,这些方法称为连接点
- 切入点 :实际被真正增强的方法
- 通知(增强):实际增强的逻辑部分。通知有多种类型:前置通知、后置通知、环绕通知、异常通知、最终通知
- 切面:把通知应用到切入点的过程,指的是一个动作
Spring框架中一般都是基于AspectJ实现AOP操作,需要引入AOP相关依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<!--AspectJ 开始-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
<!--AspectJ 结束-->
AspectJ不是Spring的组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作
AOP操作
切入点表达式作用:知道对哪个类里面的哪个方法进行增强
语法结构
execution([权限修饰符][返回类型][类全路径][方法名称][参数列表])
对com.atguigu.dao.BookDao类里面的add方法进行增强
execution(* com.atuguigu.dao.BookDao.add(..))
*表示所有修饰符,返回类型可以省略
对com.atguigu.dao.BookDao类里面的所有方法增强
execution(* com.atuguigu.dao.BookDao.*(..))
对com.atguigu.dao包里面所有类,类里面的所有方法进行增强
execution(* com.atuguigu.dao.*.*(..))
AspectJ注解
创建类,在类里面定义方法
//被增强类
@Component
public class User {
public void add(){
System.out.println("add....");
}
}
创建增强类(编写增强逻辑)
在增强类里面创建方法,让不同的的方法代表不同的通知类型
//增强类
@Component
@Aspect //生成代理对象
public class UserProxy {
//Before注解表示作为前置通知
@Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void before(){
System.out.println("before....");
} @After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void after(){
System.out.println("after...");
}
//方法返回后执行,比after早,有异常不执行
@AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void afterReturning(){
System.out.println("afterReturning...");
}
//有异常执行
@AfterThrowing(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void afterThrowing(){
System.out.println("afterThrowing...");
} //环绕通知:在方法之前和之后都通知
@Around(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("环绕之前..."); //被增强的方法执行
proceedingJoinPoint.proceed(); System.out.println("环绕之后...");
}
}
进行通知配置
在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: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"> <!--引入context/aop名称空间-->
<!--开启注解扫描-->
<context:component-scan base-package="com.atguigu.spring5"></context:component-scan>
<!--开启AspectJ生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
配置不同类型的通知
在增强类里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置。
测试
@Test
public void testAopAnno(){
ApplicationContext context = new FileSystemXmlApplicationContext("/src/main/java/bean1.xml");
User user = context.getBean("user", User.class);
user.add();
}
/*
output: 环绕之前...
before....
add....
afterReturning...
after...
环绕之后... */
相同的切入点抽取
//相同切入点抽取
@Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void pointdemo(){ } //Before注解表示作为前置通知
@Before(value = "pointdemo()")
public void before(){
System.out.println("before....");
}
有多个增强类对同一个方法进行增强,设置增强类的优先级。
在增强类上面添加注解@Order(数字)
,数字值越小优先级越高
AspectJ配置文件
AspectJ也可以通过配置文件使用
JdbcTemplate
概念
Spring框架对JDBC进行封装,使用JdbcTemplate可以很方便的实现对数据库的操作。
准备工作
引入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.23</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
在spring配置文件配置数据库连接池
配置JdbcTemplate对象,注入DataSource
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 注入dataSource -->
<property name="dataSource" ref="dataSource"></property>
</bean>
创建dao类,注入jdbcTemplate对象;创建service类,注入dao对象
CRUD操作
增删改操作使用的是
update(String sql, Object... args)
函数@Repository
public class BookDaoImpl implements BookDao{
//注入jdbcTemplate
private JdbcTemplate jdbcTemplate; //添加方法
@Override
public void add(Book book){
//1 创建sql语句
String sql = "insert into t_book values(?,?,?)";
//2 调用方法实现
Object[] args = {book.getUserId(),book.getUsername(),book.getUstatus};
int update = jdbcTemplate.update(sql,args);
}
}
查询返回某一个值
查询操作使用的是
queryForObject(String sql, Class<T> requiredType)
方法,第二个参数表示查询操作的返回类型。String sql = "select count(*) from t_book";
Integer count = jdbcTemplate.queryForObject(sql,Integer.class);
查询返回对象
queryForObject(String sql,RowMapper<T> rowMapper,Object... args)
query(String sql,RowMapper<T> rowMapper,Object... args)
:返回对象列表第二个参数rowMapper是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装
String sql = "select * from t_book where user_id=?";
//调用方法
Book book = jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<Book>(Book.class),id);
批量操作
batchUpdate(String sql,List<Object[]> batchArgs)
public void batchAdd(List<Object[]> batchArgs){
String sql = "insert into t_book values(?,?,?)";
int[] ints = jdbcTemplate.batchUpdate(sql,batchArgs);
}
Spring声明式事务
Sping事务管理介绍
事务添加到JavaEE三层结构里面Service层(业务逻辑层)
在Spring进行事务管理操作有两种方式:编程式和声明式
编程式
public void accountMoney(){
try{
//1 开启事务 //2 进行业务操作
userDao.reduceMoney(); userDao.addMoney();
//3 没有发生异常,提交事务
}catch(Exception e){
//4 出现异常 事务回滚
}
}
声明式事务管理有基于注解方式,也有基于xml配置文件方式
在Spring进行声明式事务管理,底层使用AOP原理
Spring事务管理API提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类
注解方式
在配置文件中配置事务管理器,并开启事务注解
<!-- 创建事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean> <!-- 开启事务注解 引入名称空间tx-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
在service类上面添加事务注解@Transactional
这个注解可以添加到类上面,也可以添加上方法上面,如果添加上类上面表示这个类里面所有的方法都添加事务。
这个注解里面可以配置事务相关的参数
propagation:事务传播行为。多事务方法(对数据库表中的数据发生变化的操作)进行互相调用,这个过程中事务是如何进行管理的。Spring定义了7种传播行为
isolation:事务的隔离级别,默认是可重复读
timeout:超时时间。事务需要在一定时间内进行提交,如果不提交进行回滚。默认值-1(没有超时),单位秒
readOnly:是否只读。默认false,true表示只能做查询操作
rolllbackFor:回滚。设置出现哪些异常进行回滚
noRollbackFor:不回滚。设置出现哪些异常不进行回滚
xml方式
- 配置事务管理器
- 配置通知
- 配置切入点和切面
<!--配置通知-->
<tx:advice id="txadvice">
<!--配置事务参数-->
<tx:attributes>
<!--指定哪种规则的方法上面添加事务-->
<tx:method name="accountMoney" propagation="REQUIRED"></tx:method>
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 配置切入-->
<aop:pointcut id="pt" expression="execution(* com.atguigu.spring5.service.UserService.*(..))"/>
<!-- 配置切面-->
<aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>
完全注解开发
创建配置类替代xml配置文件
@Configuration
@ComponentScan(basePackages="com.atguigu")
@EnableTransactionManagement //开启事务
public class TxConfig{
//创建数据库连接池
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.SetDriverClassName("");
dataSource.SetUrl("");
dataSource.SetUsername("");
dataSource.SetPassword("");
return dataSource;
} //创建JdbcTemplate对象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
//到ioc容器中根据类型找到dataSource
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//注入dataSource
jdbcTemplate.setDataSource(dataSource);
return jdbcemplate;
} //创建事务管理器
@Bean
public DataSourceTransactionManager getDataSourceTransationManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransationManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
Spring5新功能
整个Spring5框架的代码基于Java8,运行时兼容JDK9,许多不建议使用的类和方法在代码库中删除。
整合日志框架
Spring5自带类通用的日志封装,已经移除了Log4jConfigListener,官方建议使用Log4j2。
Nullable注解和函数式注册对象
@Nullable注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,属性值可以为空,参数值可以为空。
Spring5核心容器支持函数时风格GenericApplicationContext,通过lambda表达式注册对象
public void test(){
//1 创建GenericApplicationContext对象
GenericApplicationConntext context = new GenericApplicationContext();
//2 调用context的方法对象注册
context.refresh();
context.registerBean(User.class, ()-> new User());
//context.registerBean("user1",User.class,()->new User());
//3 获取在spring注册的对象
User user = context.getBean("com.atguigu.spring5.test.User");
//User user = context.getBean("user1");
}
支持整合JUnit5
SpringWebFlux
简介
WebFlux是Spring5添加新的模块,用于web开发,功能和SpringMVC类似,WebFlux使用当前一种比较流行的响应式出现的框架。
传统的web框架比如SpringMVC,这些基于Servlet容器,WebFlux是一种异步非阻塞的框架,异步非阻塞的框架在Servlet3.1以后才支持,核心是基于Reactor的相关API实现。
同步异步针对调用者:调用者发送请求,如果等着对方回应之后才去做其他事情就是同步,如果发送请求之后不等着对方回应就去做其他事情就是异步
阻塞和非阻塞针对被调用者:被调用者收到请求之后,做完请求任务之后才给出反馈就是阻塞,收到请求之后马上给出反馈然后再去做其他事情就是非阻塞
WebFlux特点:
- 非阻塞:在有限资源下,提高系统的吞吐量和伸缩性,以Reactor为基础实现响应式编程
- 函数式编程:Spring5框架基于java8,WebFlux使用java8函数式编程方式实现路由请求
WebFlux VS SpringMVC
- 都可以使用注解方式,都运行在Tomcat等容器中
- SpringMVC采用命令式编程,WebFlux采用异步响应式编程
响应式编程
响应式编程是一种面向数据流和变化传播的编程范式,这意味着可以在编程语言中很方便的表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
响应式编程使用观察者模式,在java8及之前的版本提供的观察者模式两个类Observer和Observerable。
在java8之后被Flow类取代
Reactor实现
响应式编程操作中,Reactor是满足Reactive规范框架,其有两个核心类Mono和Flux,这两个类实现接口Publisher,提供丰富操作符。Flux对象实现发布者,返回N个元素;Mono实现发布者返回0或1个元素。
Flux和Mono都是数据流的发布者,使用Flux和Mono都可以发出三种数据信号:元素值、错误信号、完成信号。后两个代表终止信号,用于告诉订阅者数据流结束了,错误信号在终止数据流的同时把错误信息传递给订阅者。
引入依赖
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.3.5.RELEASE</version>
</dependency>
public static void main(String[] args) {
//just方法直接声明
Flux.just(1,2,3,4);
Mono.just(1); //其他方法
Integer[] array = {1,2,3,4};
Flux.fromArray(array); List<Integer> list = Arrays.asList(array);
Flux.fromIterable(list); }
错误信号和完成信号都是终止,不能共存;如果没有发送任何元素值,而是直接发送错误或完成信号表示空数据流;如果没有错误信号,没有完成信号,表示无限数据流。
调用just或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流。
对数据流进行一道道操作,成为操作符,比如工厂流水线
map:元素映射为新元素
flatMap:把每个元素转换成流,把转换之后的多个流合并成大的流
WebFlux执行流程和核心API
WebFlux基于Reactor,默认容器是Netty,Netty是高性能的NIO框架,异步非阻塞的框架。
SpringWebFlux执行过程和SpringMVC相似:
- SpringWebFlux核心控制器DispatchHandler,负责请求的处理,实现WebHandler接口
- HandlerMapping:请求查询到处理的方法
- HandlerAdapter:真正负责请求处理
- HandlerResultHandler:响应结果处理
SpringWebFlux实现函数式编程的两个接口:RouterFunction(路由处理)和HandlerFunction(处理函数)
基于注解编程模型
使用注解编程模型方式,和之前SpringMVC使用相似,只需要把相关依赖配置到项目中,SpringBoot自动配置相关运行容器,默认情况下使用Netty服务器。
引入webflux相关依赖,创建一个SpringBoot工程
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.3.5.RELEASE</version>
</dependency>
创建实体类User,以及service类
@Service
public class UserServiceImpl implements UserService { //创建map集合存储数据
private final Map<Integer,User> users = new HashMap<Integer, User>(); public UserServiceImpl(){
this.users.put(1,new User("lucy","boy",20));
this.users.put(2,new User("mary","girl",20));
this.users.put(3,new User("jack","boy",20));
} public Mono<User> getUserById(int id) {
return Mono.justOrEmpty(this.users.get(id));
} public Flux<User> getAllUser() {
return Flux.fromIterable(this.users.values());
} public Mono<Void> saveUserInfo(Mono<User> userMono) {
return userMono.doOnNext(person ->{
//向map集合里面放值
int id = users.size();
users.put(id,person);
}).thenEmpty(Mono.empty());//Mone.empty()表示数据流结束
}
}
创建controller
@RestController
public class UserController {
@Autowired
private UserService userService; @GetMapping("/user/{id}")
public Mono<User> getUserById(@PathVariable int id){
return userService.getUserById(id);
} @GetMapping("/user")
public Flux<User> getUsers(){
return userService.getAllUser();
} @PostMapping("/saveuser")
public Mono<Void> saveUser(@RequestBody User user){
Mono<User> userMono = Mono.just(user);
return userService.saveUserInfo(userMono);
}
}
虽然形式上和SpringMVC方式差不多,但是底层不一样了。前者基于SpringMVC+Servelet+Tomcat,后者基于SpringWebFlux+Reactor+Netty。
基于函数式编程模型
在使用函数式编程模型操作时,需要自己初始化服务器。
基于函数式编程模型的时候,有两个核心接口:RouterFunction(实现路由功能,请求转发给对应的Handler)和HandlerFunction(处理请求生成响应的函数)。核心任务定义两个函数式接口的实现并且启动需要的服务器。
SpringWebFlux请求和响应不再是ServletRequest和ServeltResponse,而是ServerRequest和ServerRespone。
创建Handler
public class UserHandler { private final UserService userService;
public UserHandler(UserService userService){
this.userService = userService;
} public Mono<ServerResponse> getUserById(ServerRequest serverRequest){
int userID = Integer.valueOf(serverRequest.pathVariable("id"));
//空值处理
Mono<ServerResponse> notFount = ServerResponse.notFound().build();
Mono<User> userMono = this.userService.getUserById(userID);
//把userMono进行转换返回
//使用Reactor操作符flatMap
return userMono.flatMap(person -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
.body(fromObject(person)))
.switchIfEmpty(notFount);
} public Mono<ServerResponse> getAllUsers(ServerRequest serverRequest){
//调用service得到结果
Flux<User> users = this.userService.getAllUser();
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users,User.class);
} public Mono<ServerResponse> saveUser(ServerRequest serverRequest){
//得到user对象
Mono<User> userMono = serverRequest.bodyToMono(User.class);
return ServerResponse.ok().build(this.userService.saveUserInfo(userMono));
}
}
初始化服务器,编写Router和adapter
public class Server {
public static void main(String[] args) throws Exception {
Server server = new Server();
server.createReactorService();
System.out.println("enter to exit");
System.in.read();
} //1 创建Router路由
public RouterFunction<ServerResponse> routingFunction(){
UserService userService = new UserServiceImpl();
UserHandler userHandler = new UserHandler(userService); return RouterFunctions.route(
GET("/users/{id}").and(accept(MediaType.APPLICATION_JSON)),userHandler::getUserById
).andRoute(
GET("/users").and(accept(MediaType.APPLICATION_JSON)),userHandler::getAllUsers
); } // 2 创建服务器完成适配
public void createReactorService(){
//路由和Handler适配
RouterFunction<ServerResponse> route = routingFunction();
HttpHandler httpHandler = toHttpHandler(route);
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler); //创建服务器
HttpServer httpServer = HttpServer.create();
httpServer.handle(adapter).bindNow();
}
}
超详细!!Spring5框架开发笔记的更多相关文章
- Spring5框架学习笔记(详细)
目录 01 Spring框架概述 02 IOC容器 IOC概念和原理 IOC BeanFactory接口 IOC操作 Bean管理(概念) IOC操作 Bean管理(基于xml方式) IOC操作 Be ...
- Spring5框架学习笔记
Spring5学习笔记 介绍: 1.引入相应jar包 导入: ps:网上下载教程: https://repo.spring.io/release/org/springframework/spring/ ...
- 吴裕雄--天生自然Django框架开发笔记:Django Admin 管理工具
Django 提供了基于 web 的管理工具. Django 自动管理工具是 django.contrib 的一部分.可以在项目的 settings.py 中的 INSTALLED_APPS 看到它: ...
- 吴裕雄--天生自然Django框架开发笔记:Django 创建第一个项目
Django 管理工具 安装 Django 之后,您现在应该已经有了可用的管理工具 django-admin.可以使用 django-admin 来创建一个项目: 可以来看下django-admin ...
- 吴裕雄--天生自然Django框架开发笔记:Django简介
Python下有许多款不同的 Web 框架.Django是重量级选手中最有代表性的一位.许多成功的网站和APP都基于Django. Django是一个开放源代码的Web应用框架,由Python写成. ...
- 超详细Web前端开发规范文档
http://www.w3cfuns.com/notes/26488/c2ae788c77f835357025026a148b9863.html
- express框架开发笔记
1.express项目修改不重启 $ supervisor node bin/www 让supervisor监听模板文件的改动 $ supervisor --extensions html,css,j ...
- 吴裕雄--天生自然Django框架开发笔记:Django Nginx+uwsgi 安装配置
Django Nginx+uwsgi 安装配置 使用 python manage.py runserver 来运行服务器.这只适用测试环境中使用. 正式发布的服务,需要一个可以稳定而持续的服务器,比如 ...
- 吴裕雄--天生自然Django框架开发笔记:Django 表单
HTML表单是网站交互性的经典方式. 用Django对用户提交的表单数据进行处理. HTTP 请求 HTTP协议以"请求-回复"的方式工作.客户发送请求时,可以在请求中附加数据.服 ...
随机推荐
- oracle之用户
命令都是在命令行窗口执行 创建用户 1)登陆管理员用户 sqlplus system/密码 sqlplus system/briup 注意不要以分号结尾 2)创建用户 create user 用户名 ...
- 正则表达式匹配${key}并在Java中使用
1.正则表达式匹配${key} \$\{([a-z]+)\} 能够匹配字符串中以${key}形式的文本(其中key为小写应为字母) .*\$\{([a-z]+)\}.* 可以用来检测文本中是否有${k ...
- 40. 组合总和 II + 递归 + 回溯 + 记录路径
40. 组合总和 II LeetCode_40 题目描述 题解分析 此题和 39. 组合总和 + 递归 + 回溯 + 存储路径很像,只不过题目修改了一下. 题解的关键是首先将候选数组进行排序,然后记录 ...
- 【转载】关于grad_tensors的解惑
转载:https://www.cnblogs.com/marsggbo/p/11549631.html 平常都是无脑使用backward,每次看到别人的代码里使用诸如autograd.grad这种方法 ...
- 在windows上安装MySQL数据库注意点及Navicat Premium 15的破解
在windows上安装MySQL数据库 跟随慕课网教程(http://www.imooc.com/wiki/mysqllesson/mysqlwindows.html)下载安装MySQL: 其中注意 ...
- Java基础 随笔整理
Java基础随笔整理 为了方便阅读,特整理了相关的学习笔记 Java感想 操千曲而后晓声 Java入门 Java其他 Java虚拟机详解 语言入门百题 Java开发工具 · Eclipse Java语 ...
- 什么是ETL?
一.ETL概念之背景 随着企业的发展,目前的业务线越来越复杂,各个业务系统独立运营.例如:CRM系统只会生产CRM的 数据:Billing只会生产Billing的数据.各业务系统之间只关心自己的数据, ...
- 【Azure Developer】Python 获取Micrisoft Graph API资源的Access Token, 并调用Microsoft Graph API servicePrincipals接口获取应用ID
问题描述 在Azure开发中,我们时常面临获取Authorization问题,需要使用代码获取到Access Token后,在调用对应的API,如servicePrincipals接口. 如果是直接调 ...
- react+ts封装AntdUI的日期选择框之月份选择器DatePicker.month
需求:由于在项目开发中,当需要使用该组件时都需要对该组件进行大量的代码输出,为了方便代码统一管理,减少冗余代码,所以将此组件进行二次封装. 其他成员在使用中只需将自己的设置通过对应的参数传递到该组件, ...
- P3388 【模板】割点(割顶) 题解 (Tarjan)
题目链接 P3388 [模板]割点(割顶) 解题思路 最近学的东西太杂了,多写点博客免得自己糊里糊涂的过去了. 这个题求割点,感觉这篇文章写得挺好. 割点是啥?如果去掉这个点之后连通图变成多个不连通图 ...