Spring5
1、Spring框架概述:
1、Spring是一个轻量级的开源的javaEE框架 轻量级:内存耗费比较小,jar包需求比较少 开源:免费试用 框架:让开发更加方便,代码更加简洁
2、spring可以解决企业应用开发的复杂性
3、Spring有两个核心部分:第一个是IOC 第二个是:AOP 1、IOC:控制反转,把创建对象的过程交给Spring进行管理 2、AOP:面向切面,不修改源代码进行添加功能增强
4、Spring的特点: 1、方便解耦合,简化开发 2、AOP编程的支持 3、方便程序测试 4、方便和其他框架进行整合 5、方便进行事务操作 6、降低API开发难度 |
2、入门案例:
Spring的下载地址:repo.spring.io
1、先下载Spring 2、添加一个项目 3、导入Spring5的相关jar包
也就是四个jar包:
除了这四个包之外,还需要一个日志包: commons-logging-1.1.1.jar
4、创建普通类,在这个类中创建一个普通的方法
5、创建一个Spring配置文件,在配置文件中配置创建的对象
1)spring配置文件是使用xml文件的格式
6、测试代码的编写
public class Test01 {
|
3、IOC(概念及其原理):
1、什么是IOC:
1):ioc也叫做控制反转,把对象创建和对象之间的调用过程都交给Spring进行管理 2):使用IOC的目的是为了降低耦合度 |
2、IOC的底层原理:
1、xml解析、工厂模式、反射
图解ICO过程:
|
2、IOC(接口) 1、IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
2、Spring提供了IOC容器实现两种方式:(两个接口) 这两个接口的作用有一个共同点,就是都可以加载Spring的XML配置文件,使用工厂模式创建出bean对象。
1):BeanFactory接口 :IOC容器基本的实现:是Spring内部的使用接口,不提供开发人员进行使用 注意:使用BeanFactory这个接口的时候,在加载配置文件的时候,并不会创建出javabean对象,只有在获取或者使用(什么时候用的时候,什么时候创建)这个javabean对象的时候才会进行创建。
2):ApplictionContext接口 :BeanFactory接口的一个子接口,提供了更多更强大的功能,适用于开发人员使用。 注意:在加载配置文件的时候就会将javabean对象进行创建。
3、实现ApplictionContext接口的类主要有两个类: 1)第一个类:ClassPathXmlApplicationContext类 2)第二个类:FileSystemXmlApplicationContext类 他们的作用是加载Spring的Xml文件, 如果Spring的配置文件的路径在磁盘上,就需要使用FileSystemXmlApplication这个类(也就是Spring的配置文件的绝对路径) 如果Spring的配置文件的路径在src下,就需要使用ClassPathXmlApplicationContext类(也就是Spring的配置文件的相对路径)
|
3、IOC操作Bean管理:
1、什么是Bean管理(Bean管理指的是两个操作) 1)Spring创建对象
2)Spring进行属性的注入
2、Bean管理的操作: 1)基于Xml配置文件方式进行实现
|
外部注入Bean:
private UserDao userDao;
//创建一个set方法,便于依赖注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void test(){
userDao.test();
System.out.println("这是Service中的方法!");
}
<!--创建一个UserDao对象,因为UserDao是一个接口不能直接new对象,需要找他的实现类-->
<bean id="userDao" class="com.lilttleshark.daoimpl.UserDaoImpl"></bean>
<!--创建一个UserService对象,给属性进行赋值-->
<bean id="userService" class="com.lilttleshark.service.UserService">
<!--ref属性的作用是通过bean中的id值获取到相应的对象,再将其进行赋值到对应的属性中-->
<property name="userDao" ref="userDao"></property>
</bean>
Bean标签中的ref属性的作用是创建一个bean标签中的id对应的对象,再将这个创建的对象对当前标签中的属性进行赋值。
注入属性-内部注入:
内部bean注入:(也就是说bean标签中可以嵌套bean标签)
<!--内部注入的方式-->
<!--创建一个bean对象-->
<bean id="emp" class="com.lilttleshark.bean.Emp">
<!--通过set方法进行依赖注入-->
<property name="name" value="小王"></property>
<property name="age" value="19"></property>
<!--第三个属性是一个对象,可以通过ref外部注入的方式进行注入,也可以通过内部注入的方式-->
<property name="dept">
<bean id="dept" class="com.lilttleshark.bean.Dept">
<!--对创建的bean对象进行依赖注入-->
<property name="name" value="打工"></property>
</bean>
</property>
</bean>
注入属性-级联赋值:
级联赋值的第一种方式:(和外部注入类似)
<bean id="emp" class="com.lilttleshark.bean.Emp">
<!--通过set方法进行依赖注入-->
<property name="name" value="小王"></property>
<property name="age" value="19"></property>
<property name="dept" ref="dept"></property>
</bean>
<bean id="dept" class="com.lilttleshark.bean.Dept">
<property name="name" value="打工"></property>
</bean>
级联赋值的第二种方式:(注意,在emp这个类中需要有dept属性的get方法)
<bean id="emp" class="com.lilttleshark.bean.Emp">
<!--通过set方法进行依赖注入-->
<property name="name" value="小王"></property>
<property name="age" value="19"></property>
<property name="dept" ref="dept"></property>
<property name="dept.name" value="啥也不干"></property>
</bean>
<bean id="dept" class="com.lilttleshark.bean.Dept"></bean>
IOC操作bean管理(xml注入集合属性):
1、注入数组属性
<!--注入数组-->
<property name="course">
<array>
<value>java</value>
<value>c</value>
<value>c++</value>
</array>
</property>
2、注入List集合属性
<!--注入List集合-->
<property name="list">
<list>
<value>张山</value>
<value>张三</value>
<value>张3</value>
</list>
</property>
3、注入Map集合属性
<!--注入Map集合-->
<property name="map">
<map>
<entry key="key1" value="value1"></entry>
<entry key="key2" value="value2"></entry>
<entry key="key3" value="value3"></entry>
</map>
</property>
在集合中将泛型定义为相应的对象类型:
<!--注入List,List集合中的类型是一个对象-->
<bean id="teacher" class="com.lilttleshark.bean.Teacher">
<property name="list">
<list >
<ref bean="student1"></ref>
<ref bean="student2"></ref>
</list>
</property>
</bean>
<!--创建多个bean对象-->
<bean id="student1" class="com.lilttleshark.bean.Student">
<property name="name" value="xiaowang"></property>
</bean>
<bean id="student2" class="com.lilttleshark.bean.Student">
<property name="name" value="小王"></property>
</bean>
将集合注入的部分提取出来:
为什么要将其提取出来?
如果不将他进行提取出来,这个集合就只能在这个bean标签中使用,不能在其他地方进行操作。
操作:
1、先加入一个名称空间(util空间)
<?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 http://www.springframework.org/schema/util/spring-util.xsd">
2、使用util标签完成list集合注入的提取
<!--提取List集合类型属性注入,类似于将一个javabean对象先进行生成,然后在进行使用-->
<util:list id="animals">
<bean id="animal1" class="com.lilttleshark.bean.Animal">
<property name="name" value="狗"></property>
</bean>
<bean id="animal2" class="com.lilttleshark.bean.Animal">
<property name="name" value="猫"></property>
</bean>
<bean id="animal3" class="com.lilttleshark.bean.Animal">
<property name="name" value="熊猫"></property>
</bean>
</util:list>
<!--创建一个含有List属性的集合,使用ref将上面的list的id进行填入-->
<bean id="animalList" class="com.lilttleshark.bean.AnimalList">
<property name="list" ref="animals"></property>
</bean>
IOC操作bean管理(FactoryBean):
在Spring中有两种bean:第一种是普通的bean,第二种是工厂bean(FactoryBean)
普通bean和工厂bean的各自特点:
普通bean:在配置文件中定义的bean类型就是返回的类型
工厂bean(FactoryBean):返回的类型可以和在配置文件中定义的类型不一样
如何定义一个工厂bean对象:
1、创建一个类,让这个类实现接口FactoryBean,把这个类作为工厂bean
2、实现接口中的方法,在实现的方法中定义返回的bean对象类型
public class MyFactoryBean implements FactoryBean<Animal> {
//定义返回的bean对象
@Override
public Animal getObject() throws Exception {
return new Animal();
}
@Override
public Class<?> getObjectType() {
return null;
}
}
<!--配置FactoryBean对象-->
<bean id="myBean" class="com.lilttleshark.bean.MyFactoryBean">
</bean>
@Test
public void test06(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean1.xml");
//在spring配置文件中定义的类型是MyFactoryBean类型,但是返回的类型是一个Animal类型
Animal myBean = applicationContext.getBean("myBean", Animal.class);
myBean.setName("老鼠");
System.out.println(myBean);
}
FactoryBean对象返回的类型是通过这个类型中的getObject()方法中的返回值进行定义的。
ICO操作Bean管理——bean的作用域:
1、在Spring中,可以设置一个bean是一个单实例还是一个多实例(单实例就是只能创建一个对象,多实例就是可以创建多个对象)
2、在Spring中bean默认的情况下使用的是单实例对象
@Test
public void test05(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean1.xml");
Teacher teacher = applicationContext.getBean("teacher", Teacher.class);
Teacher teacher1 = applicationContext.getBean("teacher", Teacher.class);
System.out.println(teacher==teacher1);//true
}
说明默认情况下是单实例对象
如何设置单实例还是多实例:
通过Spring配置文件中的bean标签中的scope属性进行设置:
Scope属性的值:
1、Singleton 单实例 (默认)
2、Prototype 多实例
Singleton和prototype的区别:
1、singleton表示单实例,prototype表示多实例
2、如果为单实例,就会在加载Spring配置文件的时候进行创建对象,如果为多实例,只有在调用getBean()方法的时候才会创建对象
Bean的生命周期:
1、什么是生命周期:从对象被创建到对象被销毁的过程
2、bean的生命周期:
1、通过构造器创建bean实例(无参构造)
2、通过set方法对bean添加依赖(配置bean中的属性值)
3、调用bean中的初始化的方法(需要进行配置)
4、Bean可以进行使用(可以获取到对象)
5、当容器关闭的时,调用bean的销毁的方法(需要进行配置销毁方法)
public class Order {
private String name;
public void setName(String name) {
this.name = name;
}
public Order(){
System.out.println("第一步:调用构造方法");
}
public void initMethod(){
System.out.println("第二步:调用了初始化的方法");
}
public void destroyMethod(){
System.out.println("第五步:调用了销毁的方法");
}
}
<!--bean对象的生命周期-->
<bean id="order" class="com.lilttleshark.bean.Order" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="这是一个订单"></property>
</bean>
@Test
public void beanLifeCycle(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean1.xml");
Order order = applicationContext.getBean("order", Order.class);
System.out.println("第四步:获取到了order对象");
System.out.println(order);
//将ICO容器进行关闭
((ClassPathXmlApplicationContext)applicationContext).close();
}
结果:
第一步:调用构造方法
第二步:调用了初始化的方法
第四步:获取到了order对象
com.lilttleshark.bean.Order@609db43b
第五步:调用了销毁的方法
Bean的后置处理器:
加上bean的后置处理器之后,bean的生命周期有一下七步:
1、通过构造器创建bean实例(无参构造)
2、通过set方法对bean添加依赖(配置bean中的属性值)
3、将bean实例传递给bean的后置处理器的方法
4、调用bean中的初始化的方法(需要进行配置)
5、将bean实例传递给bean后置处理器的方法
6、Bean可以进行使用(可以获取到对象)
7、当容器关闭的时,调用bean的销毁的方法(需要进行配置销毁方法)
public class Order implements BeanPostProcessor {
private String name;
public void setName(String name) {
this.name = name;
}
public Order(){
System.out.println("第一步:调用构造方法");
}
public void initMethod(){
System.out.println("第二步:调用了初始化的方法");
}
public void destroyMethod(){
System.out.println("第五步:调用了销毁的方法");
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后置处理器postProcessBeforeInitialization执行了");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后置处理器postProcessAfterInitialization执行了");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
在spring配置文件中,一旦有一个bean标签中含有后置处理器,那么在这个配置文件中的所有bean都将会有后置处理器
IOC操作bean管理——xml自动装配:
1、什么是自动装配:根据指定装配规则(属性名或者属性类型),Spring自动将匹配的属性值进行注入
第一种自动装配:(Byname)
<!--
如何通过属性名字进行依赖注入:
在bean标签中定义属性autowire,值为ByName
需要注意的是,需要注入的属性名需要和在Spring配置文件中创建的对象的id值相同,也就是在emp这个类中的Dept这个属性的属性名需要是dept
同下面创建的Dept对象的id值相同
-->
<bean id="emp" class="com.lilttleshark.autowire.Emp" autowire="byName">
<property name="name" value="小王"></property>
</bean>
<bean id="dept" class="com.lilttleshark.autowire.Dept">
<property name="name" value="管理部门"></property>
</bean>
public class Emp {
private Dept dept;
private String name;
public void setDept(Dept dept) {
this.dept = dept;
}
public void setName(String name) {
this.name = name;
}
public class Dept {
private String name;
public void setName(String name) {
this.name = name;
}
@org.junit.Test
public void test(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("com/lilttleshark/autowire/autowire.xml");
Emp emp = applicationContext.getBean("emp", Emp.class);
emp.test();
}
第二种方法:ByType
只需要将auto-wire中的值改为ByType即可
需要注意的是:如果在Spring中对应的属性的类型有多个的话,就不能使用,因为不能确定因该使用哪一个。这时候就需要使用ByName的方式。
<bean id="emp" class="com.lilttleshark.autowire.Emp" autowire="byType">
<property name="name" value="小王"></property>
</bean>
<bean id="dept" class="com.lilttleshark.autowire.Dept">
<property name="name" value="管理部门"></property>
</bean>
IOC操作bean管理——外部属性文件:
举例:使用创建数据库连接池(需要添加druid.jar和数据库连接的jar包)
第一种方式:直接创建数据库连接池:
<!--直接配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc;mysql://localhost:3306/bookcity"></property>
<property name="username" value="root"></property>
<property name="password" value="208262"></property>
</bean>
第二种方式,引入外部属性文件配置数据库连接池:
1、创建一个properties文件进行保存数据
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/bookcity
username=root
password=208262
2、将外部的properties文件进行引入到Spring配置文件中(引入context名称空间)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd ">
<!--直接配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value=""></property>
<property name="url" value=""></property>
<property name="username" value=""></property>
<property name="password" value=""></property>
</bean>
</beans>
3、在Sping中将配置文件进行引入
<!--将properties文件进行引入-->
<context:property-placeholder location="com/lilttleshark/databaseTest/JDBC.properties"></context:property-placeholder>
<!--通过将properties文件中的内容进行读取在直接配置连接池-->
<!--通过 ${properties文件中的key} 进行获取文件中对应的value-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${dirverClassName}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
</bean>
ICO操作Bean管理——技术注解方式:
1、什么是注解:注解就是代码的解释,格式:@+注解名称(属性名称=属性值,属性名称=属性值)
2、注解可以什么时候使用:注解可以作用在类、方法、属性上面
3、使用注解的目的:简化Sping配置文件xml的配置
Sping中针对Bean管理创建对象提供的注解主要有四个:
1、@Component 常用于创建普通对象
2、@Service 常用于Service层
3、@Controller 常用于Web层
4、@Repository 常用于dao层
以上四种注解都可以创建bean对象
基于注解的方式实现对象的创建:
1、除了需要将通过XML文件的进行创建对象的五个jar包之外,还需要将aop这个jar包进行导入
spring-aop-5.3.20.jar
2、开启组件的扫描
如何开启:
1、先引入context名称空间
2、配置context:component-scan标签中的属性
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启组件扫描
如果有多个包需要扫描,两个包将通过 逗号 进行隔开 或者直接写这个包的上一级目录
-->
<context:component-scan base-package="com.lilttleshark.NoteMethod"></context:component-scan>
</beans>
3、创建一个类,在这个类上添加上注解
package com.lilttleshark.NoteMethod;
import org.springframework.stereotype.Component;
//使用注解中的value中的值就等于在bean标签中的id值
//使用注解的方式value可以不写,如果不写value,他的默认值是这个类的类名,再将首字母进行小写 在这里就是将User变成user
@Component(value = "user") //这个注解就等同于 在bean标签中的 <bean id="user" class="com.lilttleshark.NoteMethod.User">
public class User {
public void test(){
System.out.println("这个对象创建了。。。。");
}
}
public class Test {
@org.junit.Test
public void test01(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("com/lilttleshark/NoteMethod/bean.xml");
User user = applicationContext.getBean("user", User.class);
user.test();
}
}
开启组件扫描的细节配置:
<!--关于扫描的细节:
use-default-filters="false" 这个的作用是将默认使用的过滤器进行取消,这时候需要自己自定义过滤器
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
这个就是一个过滤器的创建 type表示扫描什么 annotation就是表示扫描有注解的类,
expression 就是注解是什么注解,这里写的是Component这个注解
-->
<context:component-scan base-package="com.lilttleshark.NoteMethod" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
如果是使用的默认的过滤器,就会将base-package中的类全部进行扫描
<!--因为使用的是默认的过滤器,所以会将base-package中的内容都进行扫描,但是,因为有
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
这个是exclude,意思为不包括,所以如果annotation注解为Component的类不会进行扫描
-->
<context:component-scan base-package="com.lilttleshark.NoteMethod">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
基于注解的方式实现属性的注入:
Sping中提供用作属性注入的注解:(一下是常见的三种注入属性的注解)
1、@Autowired 通过属性的类型进行注入
2、@Qualifier 通过名称进行注入(需要连同@Autowired进行使用)
3、@Resource 既可以通过属性进行注入,也可以通过名称进行注入(需要导包)
1、@AutoWired 根据属性的类型进行自动注入(XML方式中的ByType方式相似)
第一步:先创建Service和Dao对象进行创建,在Service和Dao类上添加创建对象的注解
第二步:在Service中注入Dao对象,通过使用注解@Autowired的方式进行属性的注入(注意,没有编写set方法)
package com.lilttleshark.NoteMethod.UserDao;
import org.springframework.stereotype.Repository;
@Repository
public interface UserDao {
void test();
}
package com.lilttleshark.NoteMethod.UserDao.UserDaoImpl;
import com.lilttleshark.NoteMethod.UserDao.UserDao;
import org.springframework.stereotype.Repository;
@Repository
public class UserImpl implements UserDao {
@Override
public void test() {
System.out.println("UserImpl中的test方法被调用了!");
}
}
package com.lilttleshark.NoteMethod.service;
import com.lilttleshark.NoteMethod.UserDao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
//定义Dao对象,注意,不需要set方法,这个没有定义set方法 添加注入属性的注解
@Autowired //根据类型进行注入,需要创建一个UserDao对象(已经UserDao中添加了创建对象的注解)
private UserDao userDao;
public void test(){
userDao.test();
System.out.println("Service中的test方法被调用了!");
}
}
@org.junit.Test
public void service(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("com/lilttleshark/NoteMethod/bean.xml");
//如果注解中没有进行编写value值,那么value就是将这个类的类名的首字母变成小写
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.test();
}
2、@Qualifier 根据属性名称进行注入(XML方式中的ByName方式相似)
需要注意:使用@Qualifier这个注解的时候,需要连同@Autowired同时使用
package com.lilttleshark.NoteMethod.UserDao.UserDaoImpl;
import com.lilttleshark.NoteMethod.UserDao.UserDao;
import org.springframework.stereotype.Repository;
@Repository(value = "userImpl")
public class UserImpl implements UserDao {
@Override
public void test() {
System.out.println("UserImpl中的test方法被调用了!");
}
}
@Service
public class UserService {
//定义Dao对象,注意,不需要set方法,这个没有定义set方法 添加注入属性的注解
@Autowired //根据类型进行注入,需要创建一个UserDao对象(已经UserDao中添加了创建对象的注解)
@Qualifier(value = "userImpl") //如果没有对value进行赋值,那么value的值为将类名的手写字母改为小写
private UserDao userDao;
public void test(){
userDao.test();
System.out.println("Service中的test方法被调用了!");
}
}
3、@Resource 可以根据属性类型进行注入,也可以根据名称进行注入
如果@Resource注解中没有属性值,就表示根据属性类型进行注入
如果@Resource注解中有name属性和属性值,就表示根据名称进行注入
需要注意:@Resource这个注解是在javax.annotation.Resource这个jar中的,不是属于Sping中的。如果需要使用,就需要javax.annotation.Resource这个包
第一种有name属性的注入方式(通过名称进行注入):
@Repository(value = "userImpl")
public class UserImpl implements UserDao {
@Override
public void test() {
System.out.println("UserImpl中的test方法被调用了!");
}
}
@Resource(name = "userImpl")
private UserDao userDao;
public void test(){
userDao.test();
System.out.println("Service中的test方法被调用了!");
}
@org.junit.Test
public void service(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("com/lilttleshark/NoteMethod/bean.xml");
//如果注解中没有进行编写value值,那么value就是将这个类的类名的首字母变成小写
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.test();
}
第二种方法注解中没有name属性(通过属性的类型进行注入的方式):
@Resource
private UserDao userDao;
public void test(){
userDao.test();
System.out.println("Service中的test方法被调用了!");
}
4、@Value 注入普通类型的属性
像一个普通的属性注入属性值:
@Value(value = "小王")
private String name;
这样就将小王这个字符串注入进了name属性
完全注解开发:
1、创建配置类,替代xml配置文件
package com.littleshark.ConfigByClass;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
//这是一个配置类,作用是替代xml配置文件,也就是说,可以通过这个类完成在xml文件中的配置
@Configuration //定义这是一个配置类
@ComponentScan(basePackages = {"com.littleshark"}) //定义组件扫描器 等同于xml文件中的:
// <context:component-scan base-package="com.littleshark"></context:component-scan>
public class SpringConfig {
}
2、编写一个测试方法(因为使用配置类替代了配置文件xml,所以不能再使用加载xml文件的方式进行引入配置,需要使用另一种方式进行加载配置)
最终纯注解的方式的代码:
package com.littleshark.ConfigByClass;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
//这是一个配置类,作用是替代xml配置文件,也就是说,可以通过这个类完成在xml文件中的配置
@Configuration //定义这是一个配置类
@ComponentScan(basePackages = {"com.littleshark"}) //定义组件扫描器 等同于xml文件中的:
// <context:component-scan base-package="com.littleshark"></context:component-scan>
public class SpringConfig {
}
package com.littleshark.ConfigByClass.bean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
//添加注解,是之额能够创建对象
@Component //创建普通类 没有value属性值,默认是将类名的首字母变成小写后的字符串
public class Animal {
//添加注解,实现以来添加,通过名称的方式进行注入
@Autowired
@Qualifier(value = "cat") //需要连同@Autowired进行使用
private Cat cat;
public void test(){
cat.test();
}
}
package com.littleshark.ConfigByClass.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component //创建普通的对象 没有添加value属性值,默认是将类名的首字母变成小写的字符串
public class Cat {
//添加注解,进行依赖添加
@Value(value = "小王")
private String name;
public void test(){
System.out.println(this.name+" 喵喵喵!");
}
}
package com.littleshark.ConfigByClass.test;
import com.littleshark.ConfigByClass.SpringConfig;
import com.littleshark.ConfigByClass.bean.Animal;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test {
@org.junit.Test
public void test01(){
ApplicationContext applicationContext=new AnnotationConfigApplicationContext(SpringConfig.class);
Animal animal = applicationContext.getBean("animal", Animal.class);
animal.test();
}
}
4、AOP:
什么是AOP:
面向切面编程(方面),利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率。
不改动源代码的情况下添加新的功能,这就是AOP
AOP的作用:降低耦合度
举例:
AOP的底层原理:
1、AOP底层使用动态代理
有两种情况的动态代理:
第一种:有接口的情况,使用JDK动态代理
接口是找这个接口的实现了的对象最为代理对象
第二种:没有接口的情况,使用CGLIB动态代理
没有接口是找的这个类的子类的对象作为代理对象
AOP——JDK动态代理:
1、使用JDK动态代理,是以哦那个Proxy类中的newProxyInstance()方法进行创建动态代理对象
NewProxyInstance方法中的三个参数:
1、ClassLoader 类加载器
2、Interface (接口)增强方法所在的类,这个类实现的接口,支持多个接口
3、InvocationHandler 实现InvocationHandler这个接口,创建代理对象,写增强的方法
使用JDK动态代理的实例:
package com.littleshark.aop.JDKDynamicProxy;
public interface UserDao {
int add(int a,int b);
void update(String name);
}
package com.littleshark.aop.JDKDynamicProxy;
public class UserDaoImpl implements UserDao{
@Override
public int add(int a, int b) {
return a+b;
}
@Override
public void update(String name) {
System.out.println(name);
}
}
package com.littleshark.aop.JDKDynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class JDKProxy {
public static void main(String[] args) {
Class[] classes={UserDao.class};
UserDao o = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), classes, new UserDaoProxy(new UserDaoImpl()));
System.out.println(o instanceof UserDaoImpl);
int add = o.add(1, 2);
System.out.println(add);
/* 输出结果:
false
这是增强的代码!在方法之前,method=add这个方法的参数为:[1, 2]
这是增强的代码!在方法之后,method=add
3
*/
}
}
class UserDaoProxy implements InvocationHandler{
//因为我们在执行我们增强的代码的时候,也需要将之前方法中的代码进行执行,如果想要执行就需要将这个代理对象进行传入
//如何将代理对象进行传入,可以通过构造方法的方式进行传入
private Object object;
//将出入的代理对象进行赋值
public UserDaoProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法之前
System.out.println("这是增强的代码!在方法之前,method="+method.getName() +"这个方法的参数为:"+ Arrays.toString(args));
Object result = method.invoke(object, args);
//方法之后
System.out.println("这是增强的代码!在方法之后,method="+method.getName());
return result;
}
}
输出结果:
/* 输出结果:
false
这是增强的代码!在方法之前,method=add这个方法的参数为:[1, 2]
这是增强的代码!在方法之后,method=add
3
*/
可以看出使用JDK动态代理,创建对来的代理对象就是一个接口的子类型对象(接口不能创建对象),这个接口的子类型对象不属于我们定义的这个接口的子类型,属于另一个类型,一个不是我们编写的实现这个接口的类型,但是虽然不是我们进行定义的,但是他里面有我们进行定义的方法。
用例子解释上述解释:
上述的代码中定义了一个接口UserDao,我们要创建一个代理对象,也就是一个实现了这个接口的对象,我们自己编写了一个实现了UserDaoImpl实现了UserDao接口,实现了相应的抽象方法,但是,我们通过JDK动态代理创建出的对象不是属于我们编写的UserDaoImpl这个类的对象,因为返回的结果为false,但是这个对象是属于UserDao这个接口的,接口不能创建对象,所以,这一定是一个不是我们自己定义的类型,是使用JDK动态代理自动生成的一个类型,这个类型一定实现了UserDao这个接口,实现了这个接口的抽象方法,实现的抽象方法是和我们传入的UserDao实现类相同的代码,也就是自动生成的类中的add方法和我们编写的add方法中的代码是相同的。
AOP的相关术语:
1、连接点 在类中哪些方法可以被增强,可以被增强的方法就叫做连接点(关键词:可以被增强)
2、切入点 在类中,真正被增强的方法,这个方法就叫做切入点(关键词:正真被增强)
3、通知(增强) 实际增强的部分就是通知,也就是添加的代码(关键词:实际增强,代码)
通知有多种类型:
1、前置通知 在原来方法之前进行添加代码 @before
2、后置通知 在原来方法之后进行添加代码 @AfterRuntering
3、环绕通知 在原来方法前面和后面都添加代码 @Around
4、异常通知 在原来的方法发生异常后添加执行的代码 @AfterThrowing
5、最终通知 和try... catch..中的finally语句块类似,一定会执行的添加的代码 @After
4、切面 是一个动作,将通知应用到切入点的过程叫做切面
AOP操作——准备工作:
1、Spring框架中一般基于AspectJ实现AOP的操作
什么是AspectJ:
AspectJ不是Spring中的组成部分,他独立于Spring框架,是一个独立的框架,但是一般将AspectJ框架和Spring框架进行联合使用,进行AOP操作。
通过AspectJ实现AOP操作的方式:
1、通过xml配置文件进行实现
2、通过注解方式进行事项(常用)
2、在项目工程中引入AOP相关的依赖
主要包括一下部分:之前的AOP这个jar包和AspectJ这个jar包和AspectJ需要的jar包
spring.aspects-5.3.20.jar、spring-aop.jar、com.springsource.org.aopalliance.jar、com.springsource.org.aopalliance.jar、com.springsource.net.sf.cglib.jar
3、切入点表达式:
切人点表达式的作用:知道哪个类里面的那个方法就进行增强
语法结构:
Execution([权限修饰符] [ 返回类型] [类的全类名] [方法名称] [参数列表])
举例1:
对com.littleshark.dao.UserDao中的add方法进行增强的操作:
Execution(* com.littleshark.dao.UserDao.add(..))
*表示任意权限修饰符,返回的类型可以不写,(..)表示参数列表
举例2:
对com.littleshark.dao.UserDao中的所有方法进行增强的操作:
Execution(* com.littleshark.dao.UserDao.*(..))
*表示任意权限修饰符,返回的类型可以不写,(..)表示参数列表
举例3:
对com.littleshark.dao中的所有类中的所有方法进行增强的操作:
Execution(* com.littleshark.dao.*.*(..))
*表示任意权限修饰符,返回的类型可以不写,(..)表示参数列表
AOP——通过注解方式操作AspectJ:
1、创建一个类,在类中定义方法
2、创建一个增强类(编写增强的代码)
3、进行通知的配置
1)zaiSpring的配置文件中开启注解扫描
2)使用注解创建User和UserProxy对象
3)在增强类上面添加注解注解@Aspect
4)在Spring配置文件中开启生成代理对象
5)配置不同类型的通知
1、在增强类里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
示例代码:
package com.littleshark.aop.annotation;
import org.springframework.stereotype.Component;
@Component
public class User {
public void add() throws Exception {
System.out.println("这是User中的add方法!");
throw new Exception();
}
}
package com.littleshark.aop.annotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
//被增强的类
@Component
@Aspect
public class UserProxy {
//如果我们将这个test方法作为前置通知就需要使用@Before注解
//execution(* com.littleshark.aop.annotation.User.add(..))这是一个切入点表达式
@Before("execution(* com.littleshark.aop.annotation.User.add(..))")
public void test(){
System.out.println("这是UserProxy中的test方法!使用的是前置通知!");
}
//最终通知 出现异常任然会执行
@After("execution(* com.littleshark.aop.annotation.User.add(..))")
public void after(){
System.out.println("这是最终通知!");
}
//后置通知 如果出现了异常就不会执行
@AfterReturning(value = "execution(* com.littleshark.aop.annotation.User.add(..))")
public void afterReturn(){
System.out.println("这是后置通知!");
}
//异常通知
@AfterThrowing(value = "execution(* com.littleshark.aop.annotation.User.add(..))")
public void AfterThrow(){
System.out.println("这是异常通知");
}
//环绕通知
@Around("execution(* com.littleshark.aop.annotation.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("这是环绕通知!环绕之前");
//这里就是原方法执行的地方,通过使用ProceedingJoinPoint对象进行调用 ProceedingJoinPoint翻译为继续切入点 proceed翻译为进行
proceedingJoinPoint.proceed();
System.out.println("这是环绕通知!环绕之后");
}
}
<?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:component-scan base-package="com.littleshark.aop.annotation">
</context:component-scan>
<!--开启AspectJ生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!--它的作用是通过注解扫描,如果发现类上面有注解@Aspect这个注解,就会将他创建的对象转化成代理对象-->
</beans>
public class Test {
@org.junit.Test
public void test01(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("com/littleshark/aop/annotation/bean.xml");
User user = applicationContext.getBean("user", User.class);
try {
user.add();
} catch (Exception e) {
e.printStackTrace();
}
}
}
将公共的切入点进行抽取:
通过定义一个方法,在方法上添加一个注解叫做@Pointcut注解,注解中的value属性值填写切入点表达式,在通知配置中,只需要在其注解上value中填入定义的方法进行了
//抽取公共的切入点
@Pointcut(value = "execution(* com.littleshark.aop.annotation.User.add(..))")
public void pointCut(){
}
//如果我们将这个test方法作为前置通知就需要使用@Before注解
//execution(* com.littleshark.aop.annotation.User.add(..))这是一个切入点表达式
@Before(value="pointCut()")
public void test(){
System.out.println("这是UserProxy中的test方法!使用的是前置通知!");
}
有多个增强类,对同一个方法进行增强,可以设置增强类的优先级:
操作:
在增强类中添加一个注解@Order(数字) 数字越小,优先级越高
AOP操作——通过AspectJ配置文件进行操作:
1、创建两个类,增强类和被增强类,创建方法(切入点)
package com.littleshark.aop.xml;
public class User {
public void eat(){
System.out.println("这是在User中的eat方法!");
}
}
package com.littleshark.aop.xml;
public class UserProxy {
public void proxyMethod(){
System.out.println("这是一个前置通知!");
}
}
2、在Spring配置文件中创建两个对象
<!--创建两个类的对象-->
<!--被增强的类-->
<bean id="user" class="com.littleshark.aop.xml.User"></bean>
<!--增强类-->
<bean id="userProxy" class="com.littleshark.aop.xml.UserProxy"></bean>
3、在Spring配置文件中配置切入点,配置通知位置,配置通知
<!--配置AOP的增强-->
<aop:config>
<!--配置切入点 id中的值表示切入点的别名-->
<aop:pointcut id="pointcut" expression="execution(* com.littleshark.aop.xml.User.eat(..))"/>
<!--配置切面 这是一个动作,将通知作用到切入点中 ref中表示的是增强类-->
<aop:aspect ref="userProxy">
<!--配置增强作用在具体的方法上 method中表示需要在原切入点的那个位置进行执行的方法(也就是通知)-->
<aop:before method="proxyMethod" pointcut-ref="pointcut"></aop:before>
</aop:aspect>
</aop:config>
完全注解的方式通过AspectJ管理AOP:
1、创建一个配置类
package com.littleshark.aop.annotation.completAnnotation;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
//这是一个配置类
@Configuration //翻译为配置 说明这是一个配置类
@ComponentScan //翻译为组件扫描 添加组件扫描
@EnableAspectJAutoProxy(proxyTargetClass = true) //翻译为确保AspectJ自动代理 也就是等同于xml文件中的<aop:aspectj-autoproxy></aop:aspectj-autoproxy>这段代码
public class Config {
}
2、创建普通类和增强类,添加创建对象的注解
package com.littleshark.aop.annotation.completAnnotation;
import org.springframework.stereotype.Component;
@Component //默认value为这个类的类名首字母小写的字符串 创建对象
public class User {
public void test(){
System.out.println("这是User中的test方法!");
}
}
package com.littleshark.aop.annotation.completAnnotation;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class UserProxy {
//将公共的切入点进行抽取
@Pointcut(value = "execution(* com.littleshark.aop.annotation.completAnnotation.User.test(..))")
public void pointcut(){}
//添加一个前置通知
@Before(value="pointcut()")
public void strengthMethod(){
System.out.println("这是增强的方法!");
}
}
3、在增强类中添加@Aspect注解
4、通知的配置
测试的代码:
package com.littleshark.aop.annotation.completAnnotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test {
@org.junit.Test
public void test(){
ApplicationContext applicationContext=new AnnotationConfigApplicationContext(Config.class);
User user = applicationContext.getBean("user", User.class);
user.test();
}
}
5、jdbcTemplate:
什么是jdbcTemplate:Spring对JDBC进行封装,使用jdbcTemplate可以很方便的实现对数据库操作。
如何使用jdbcTemplate:
1、引入相关的jar包
2、在spring配置文件中配置数据库连接池
可以通过引入一个context名称标签将一个存有相应信息的propertites文件进行引入
在将properties文件中的信息进行填写进入创建数据库连接池bean的属性中。
如下图:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--通过context名称空间将properties文件进行引入配置文件-->
<context:property-placeholder location="com/littleshark/jdbcTemplate/jdbc.properties"></context:property-placeholder>
<!--创建一个数据库连接池对象-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driverClassName}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="password"></property>
</bean>
</beans>
3、配置jdbcTemplate对象,注入数据库连接池对象
<!--创建一个jdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--将数据据库连接池对象注入 查看源码后发现有参构造中是通过setting进行注入数据库连接池对象-->
<property name="dataSource" ref="dataSource"></property>
</bean>
4、创建service类,创建dao类,在dao注入jdbcTemplate对象
通过jdbcTemplate中的update方法实现对数据库的增删改:
注意:username和password在mysql中是一个关键字,在properties中尽量不要使用这两个字段作为key否则可能会出现:errorCode 1045, state 28000这样的错误
通过jdbcTemplate中的update进行增删改的操作
package com.littleshark.jdbcTemplate.dao.daoimpl;
import com.littleshark.jdbcTemplate.bean.User;
import com.littleshark.jdbcTemplate.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class UserDaoImpl implements UserDao {
//注入属性 jdbcTemplate对象
@Autowired //通过属性的类型进行注入,在xml配置文件中创建了这个对象,只有一个这样类型的对象,就会直接将xml配置文件中创建的jdbcTemplate对象直接进行注入
private JdbcTemplate jdbcTemplate;
@Override
public void add(User user) {
String sql="insert into t_user(id, username, email) value (?,?,?)";
int i = jdbcTemplate.update(sql, user.getId(), user.getUsername(), user.getEmail());
System.out.println("有"+i+"行受到了影响!");
}
@Override
public void update(User user) {
String sql="update t_user set username=? ,email=? where id=?";
int update = jdbcTemplate.update(sql, user.getUsername(), user.getEmail(), user.getId());
System.out.println("受影响的行数为:"+update);
}
@Override
public void delete(int id) {
String sql="delete from t_user where id=?";
int update = jdbcTemplate.update(sql, id);
System.out.println("受影响的行数为:"+update);
}
}
在进行使用的时候,应该先进行配置文件的加载(将属性进行注入),再获取到Service对象,通过这个对象执行dao中的方法。
@Test
public void add() {
//将配置文件进行加载
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("com/littleshark/jdbcTemplate/bean.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.add(new User(1,"xiaowang","[email protected]"));
}
通过jdbcTemplate中的queryForObject方法操作数据库查询返回某一个值(读取的是某一个字段的内容):
注意:这个值指的是查询出来的数据,例如:select count(*) from table;
他就可以返回一共有多少条数据。
通过使用queryForObject()这个方法进行获取
其中queryForObject()方法中具有两个参数:
第一个参数:sql语句
第二个参数:需要返回的类型
通过jdbcTemplate中的queryforObject方法进行操作数据库查询返回对象:
注意:这里的queryForObject方法是一个方法重载的方法,具有三个参数:
第一个参数:String sql sql语句
第二个参数:RowMapper RowMapper是一个接口,使用这个接口的实现类完成数据库的封装(使用BeanPeopertyRowMapper这个子类)
第三个参数:Object... args sql中需要的参数
具体代码:
@Override
public User getOneUser(Integer id) {
String sql="select * from t_user where id = ?";
User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), id);
return user;
}
将一个数据库中保存的数据封装成一个对象,例如在数据库中有一个表为user,我们在Java中创建一个相同的bean对象,将数据库表中的字段名作为bean的属性,在通过jdbcTemplate中的方法就可以将在数据库中查询到的数据封装成一个user对象
通过jdbcTemplate进行操作数据库查询多个对象返回集合:
返回List集合:通过使用jdbcTemplate中的query方法进行查询并返回。
Query方法中的三个参数:
第一个参数:String sql sql语句
第二个参数:RowMapper 这个接口,可以直接使用BeanPropertyRoeMapper这个类
第三个参数:sql语句中的参数
示例代码:
@Test
public void queryForList(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("com/littleshark/jdbcTemplate/bean.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
List<User> allUser = userService.getAllUser();
System.out.println(allUser);
}
JdbcTemplate操作数据库——批量操作:
什么是批量操作:一次操作数据库中的多条记录叫做批量操作。
批量的增删改都是通过jdbcTemplate中的BathUpdate方法进行操作
JdbcTemplate中的BathUpdate的参数有两个:
第一个参数:执行的sql语句
第二个参数:一个存有数据可信息的List,List集合中保存的对象是一个Object数组,这个数组主要是用来表示数据库中每一条记录的所有数据,例如在t_user表中有三个字段,所以一个Obejct数组就是{id,username,email},再将这个object数组作为List集合中的一个元素。
1、使用jdbcTemplate实现批量添加记录:
实现批量添加的代码:
2、使用jdbcTemplate实现批量修改记录:
和实现批量添加的代码相差不大,就只是将Object[]数组中的内容进行修改,sql语句的变化
具体代码如下:
3、使用jdbcTemplate实现批量删除记录:
一下是实现批量修改的代码:
事务:
什么是事务:事务是在数据库操作中最基本的单位,逻辑上统一执行,要么通过一成功,要么通过一失败。
典型场景:(银行转账场景)
没有使用事务的时候会出现的问题:
如果在添加和减少之间出现了异常,那么就会导致这个机制出现错误,所以需要事务机制进行解决这个问题。需要先将事务自动提交进行关闭出现了异常就进行回滚操作,如果都成功了就执行事务提交操作
事务的四大特性:(ACID特性)
1、原子性 指的是事务是一个任务完成的最基本单位,要么统一失败,要么同同一成功
2、一致性 指的是在实务操作之后,总量是不变的
3、隔离性 多个事务同时操作的时候,事务与事务之间不会产生联系
4、持久性 一旦事务被提交之后,数据就保存进了数据库中,数据库中的数据有持久性
事务操作(搭建事务操作的环境)
JavaEE中的三层结构:
Web层 Service层 Dao层 业务操作 数据库操作不写业务 |
事务操作:——Spring事务管理的介绍
1、一般的,都将实务操作添加到javaEE三部分的Service层(业务逻辑层)
如何在spring中进行事务管理操作:
主要有两种方式:
第一钟方式:编程式事务管理
编写逻辑代码
第二种方式:声明式事务管理(常用)
声明式事务管理有两种方式:
1、基于注解方式
2、基于xml配置文件方式
在spring中进行声明式事务管理,底层使用AOP(面向切面,不改变源代码的情况下进行功能的添加)
在spring事务管理中需要的API:
1、提供了一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类
PlatformTransactionManager这个接口的实现类如下:
事务操作——注解声明式事务管理:
1、在Spring配置文件中配置事务管理器
2、在spring配置文件中开启事务注解
1、先要在spring配置文件中引入名称空间tx
2、开启事务注解
3、在Service类上面添加(获取service类里面方法上面)添加事务注解——@Transactional
这个注解可以添加到类上面,也可以添加到方法上面:
1、添加到类上面:表示这个类中的所有方法都开启了事务
2、添加到方法上面:表示只有这个方法开启了事务
整个xml文件的代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--开启组件扫描-->
<context:component-scan base-package="com.littleshark.jdbcTemplate.affairs"></context:component-scan>
<!--将property文件进行引入-->
<context:property-placeholder location="com/littleshark/jdbcTemplate/affairs/jdbc.properties"></context:property-placeholder>
<!--创建数据库连接池对象-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driverClassName}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${thename}"></property>
<property name="password" value="${thepassword}"></property>
</bean>
<!--创建一个jdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--将上面的dataSource数据库连接池对象进行传入-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--创建一个事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--传入数据库连接池-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启事务注解 需要传入一个事务管理器对象-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
值得一说的是:
如果在xml配置了事务,并且在service类或者方法中添加了事务的注解,那么就不需要要在编写事务回滚代码和事务提交代码,全部有spring进行管理。(很方便)
实务操作——声明式事务管理的参数配置:
常用的参数:
1、Propagation:事务的传播行为
多事务方法直接被调用,这个过程的事务是如果进行管理的
解释:
1、事务方法:对数据库中的数据进行操作的方法(例如添加方法,删除方法等)
2、多事务方法直接被调用:一个事务方法的方法体中调用另一个事务方法。
Spring框架事务传播行为有七种:
实例:
@Transactional
Public void add(){ public void update(){
//调用update方法
Update();
} }
使用required:
如果add方法本身是存在事务的,那么在调用update方法的时候,update方法也会使用add方法种的事务
如果add方法本身是不存在事务的,那么在调用update方法的时候,add会创建一个新的事务。
使用required_New:
如果使用add方法调用update方法,无论add方法本身有没有事务,都会去创建一个新的事务
如果我们没有进行配置注解@Transactional种的属性值,默认使用的是required
@Transactional(propagation = Propagation.REQUIRED)
2、Isolation:事务的隔离级别
事务有特性称为隔离性,多事务操作相互之间不会产生影响,如果没有隔离性就会产生很多问题
产生的问题包括:
1、三个读的问题:脏读、不可重复读、虚读(幻读)
脏读:一个事务可以读到另一个未提交的事务的数据,叫做脏读
不可重复读:一个未提交的事务读到了一个提交的事务修改数据
幻读:一个未提交的事务,读到了一个提交事务添加的数据
可以通过设置隔离级别的方式进行解决问题
有以下四种隔离界别:
设置隔离级别:
注意:在mysql中默认的隔离级别就是Repeatable Read可重复读隔离级别
3、readOnly:是否只读
读,一般指的是查询操作 写:增删修改操作
在spring5框架中,readOnly的默认属性值为false,也就是说,在这个事务中,你既可以进行写的操作,也可以进行差的操作。
如果将readOnly的属性值修改为true,那么在这个事务中你只能进行查询语句的操作,不能修改数据库中的数据。
4、RollbackFor:回滚
设置在事务方法中出现了那些异常之后进行回滚操作
5、noRollbackFor:不回滚
设置出现了异常之后不进行回滚操作
6、timeOut:事务的超时时间
事务需要在一定时间内进行提交,不能一直处于事务中,如果不进行提交,就自动进行回滚。
在spring框架中,默认的超时时间为-1,也就是不超时,但是我们可以通过设置进行修改超时时间。(单位为秒)
实例:将超时时间修改为10秒
事务操作——xml声明式事务管理:
提示:在事务方法中添加事务的操作,等同于将事务方法进行加强,以下就是通过将事务方法进行加强的方式将事务操作添加的事务方法和事务类上的。
步骤:
1、配置事务管理器
2、配置通知(这里要增强的部分就是事务操作)
注意:在配置通知的时候,因为这里的通知是是一个事务,所以需要将事务的属性进行配置
有关于事务的操作都在tx名称空间中
3、配置切入点和切面(切入点就是要增强的方法,切面是值将通知添加进切入点的操作)
<!--配置切入点和切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.littleshark.jdbcTemplate.affairs.xml.service.UserService.*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/> <!--将txAdvice中的事务添加到切入点中-->
</aop:config>
在这这里用到了切入点表达式:
Execution([权限修饰符] [ 返回类型] [类的全类名] [方法名称] [参数列表])
举个例子:
我们需要将com.littleshark.service中的UserDao这个属性的add方法作为切入点:
Execution(* com.littleshark.service.UserDao.add(..))
其中* 表示所有的所有的权限修饰符 (..)表示这个方法中的参数
通过xml声明式事务管理的xml配置如下:
事务操作——完全注解声明式事务管理:(配置类)
代码如下:
配置类的代码:
package com.littleshark.jdbcTemplate.affairs.completeAnnotation;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
@Configuration //表示这是一个配置类
@ComponentScan(basePackages = {"com.littleshark.jdbcTemplate.affairs.completeAnnotation"}) //开启组件扫描
@EnableTransactionManagement //开启事务
public class Config {
//创建一个数据库连接池对象
@Bean
public DruidDataSource getDruidDataSource(){
//将配置文件进行引入
Properties properties = new Properties();
try {
properties.load(new FileInputStream("src/com/littleshark/jdbcTemplate/affairs/completeAnnotation/jdbc.properties"));
} catch (IOException e) {
e.printStackTrace();
}
DruidDataSource druidDataSource=new DruidDataSource();
druidDataSource.setUsername(properties.getProperty("TheUsername"));
druidDataSource.setPassword(properties.getProperty("ThePassword"));
druidDataSource.setUrl(properties.getProperty("url"));
druidDataSource.setDriverClassName(properties.getProperty("driverClassName"));
return druidDataSource;
}
//创建一个jdbcTemplate对象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
//因为我们之前在上面已将数据库连接池对象进行创建了,对象存储在ioc容器中,所以我们只需要将ioc容器中的dataSource进行使用就行了
//方法中添加参数,就等同于在属性中使用了autowired注解,通过属性类型进行注入
JdbcTemplate jdbcTemplate=new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//创建一个事务管理器
@Bean
public DataSourceTransactionManager getTransactionManager(DataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager=new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
其他的和使用注解的方式没有区别。
Spring5中的新特性:
1、整个spring5框架代码基于java8,运行时兼容java9,许多不建议使用的类和方法在代码库中都进行删除了。
2、Spring5中自带了通用的日志封装
Spring5中已将Log4JConfigListener进行移除(推荐使用Log4j2)
如何整合Log4j2:
1、引入相关的依赖。
2、创建Log4j2的配置文件(log4j2.xml文件 固定文件名)
Log4j2.xml配置文件的一般结构:
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出-->
<configuration status="INFO">
<!--先定义所有的appender-->
<appenders>
<!--输出日志信息到控制台-->
<console name="Console" target="SYSTEM_OUT">
<!--控制日志输出的格式-->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</console>
</appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
<!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出-->
<loggers>
<root level="info">
<appender-ref ref="Console"/>
</root>
</loggers>
</configuration>
3、Spring5框架核心容器支持@Nullable注解
@Nullable注解是什么:
@Nullable注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,使用在参数上面表示参数可以为空,使用在属性上面,表示属性值可以为空。
4、Spring5核心容器支持函数式风格GenerricApplicaitonConetxt:
实例:
5、spring5框架支持整合Junit5:
1、Spring5整合Junit4:
1)将Junit4的依赖进行引入
Junit4的jar包
和spring中的test包
1)创建测试类,使用注解的方式完成
代码如下:
2、Spring5整合Junit5:
1)引入Junit5的相关依赖
Junit5的jar包
和spring中的test包
代码如下:
Junit5的jar可以通过idea进行下载
如果使用的是Junit5测试包的话,可以使用一个复合注解代替上面的两个注解:
使用@SpringJunitConfig(location=“classpath:bean.xml”)的方式
代码如下:
6、SpringWebflux:
1)SpringWebflux的基本介绍
1、是Spring5中的新模块,用于web开发的,功能和SpringMVC类似的,Webflux使用当前一种比较流行的相应式编程出现的框架
2、使用传统的web框架,比如使用SpringMVC,这些都是基于Servlet容器,Webflux是一种异步阻塞的框架,异步非阻塞的框架在Servlet3.1之后才支持的,其核心是基于Reactor的相关API进行实现的。
3、解释什么是异步非阻塞:
异步:表示调用者发送一个请求,就直接执行下面的代码操作了,等到服务器将信息进行返回,再将信息进行操作(发送请求之后,不需要等待对方的响应,直接去做其他的事)
同步:表示调用者发送一个请求之后,在接收到服务器的响应之后在进行执行下面的代码(发送请求之后,等到对象响应之后再去做其他的事)
同步和异步都是针对于调用者来说的
阻塞:在调用者发送完请求之后,在执行完需要执行的代码之后在进行反馈,这就是阻塞
非阻塞:在调用者发送完请求之后,直接做出了反馈,然后再去执行需要执行的代码,这就是非阻塞
阻塞和非阻塞是针对于被调用者来说的
4、Webflux的特点:
第一:非阻塞式:在有限的资源下,提高系统的吞吐量和伸缩性
第二:函数式编程:Spring5框架基于java8,Webflux使用java8函数式编程方式实现路由请求。
4、比较SpringMVC和SpringWebflux:
5、
两个框架都可以使用注解的方式,都可以运行在TomCat服务器上
SpringMVC采用的是命令式编程,Webflux采用的是异步响应式编程
2)相应式编程
什么是响应式编程:响应式编程是一种面向数据流和变换传播的编程范式,这意味着可以在编程语言中很方便的表达静态或动态的数据流,而相关的计算模板会自动将变化的值通过数据流进行传播。
Java8及其之前的版本提供了一个观察者模式的两个类:Observer和Observable
Observer的实例代码:
可以在addObserver中进行添加需要发生改变的部分:
演示Flux和Mono:
第一步:将依赖使用maven进行引入
第二步:进行代码的编写:
注意:如果你只是进行发出(发布)了数据流,没有进行订阅,那么数据流并不会进行触发,唯有在订阅之后才能将发布的数据流进行触发。
使用subscriber进行订阅操作。
操作符:
对数据流进行一道道操作,成为操作符,例如工厂的流水线
常见的操作符:
1、Map 将元素映射为一个新的元素
例如:有三个元素,分别为1、2、3,使用map将每一个元素都进行平方的操作,就将原来的元素转化成了新的元素1、4、9
2、flatMap 将元素映射成为一个流
这里只是举了一个例子:这里的组合具有不确定性
将上面的每一个元素都进行转化为流的形式,再将全部的元素进行整合在一起组成一个大的流
理解清楚什么是Publisher和Subscriber操作,以及Flux和Mono的作用:
可以将Publisher形象的理解为是一个发布者,Subscriber是一个订阅者,只有当有订阅者的时候,发布者才会有意义,当然,要存在发布者,订阅者才会有所可得,其中Flux和Mono就是Publisher中的Reactor中进行发布的方法
以下来自于网路的形象解释:
A1-A9就可以看做Publisher<T>及其提供的元素序列。A10-A13分别是求和函数SUM(A1:A9)、平均函数AVERAGE(A1:A9)、最大值函数MAX(A1:A9)、最小值函数MIN(A1:A9),可以看作订阅者Subscriber。假如说我们没有A10-A13,那么A1-A9就没有实际意义,它们并不产生计算。这也是响应式的一个重要特点:当没有订阅时发布者什么也不做。
而Flux和Mono都是Publisher<T>在Reactor 3实现。Publisher<T>提供了subscribe方法,允许消费者在有结果可用时进行消费。如果没有消费者Publisher<T>不会做任何事情,他根据消费情况进行响应。 Publisher<T>可能返回零或者多个,甚至可能是无限的,为了更加清晰表示期待的结果就引入了两个实现模型Mono和Flux。
其中Flux的作用示意图:
Mono的作用示意图:
他的作用和Flux的作用相同,但是Mono只能返回一个或者0个元素。
需要注意:Mono和Flux都会被OnComplain(完成信号)和OnError(错误信号)进行终止
如果没有错误信号和完成信号,表示无限数据流
3)响应式编程(Reactor实现)
1、响应式编程操作中,Reactor是满足Reactive规范框架
2、Reactor有两个核心类,Mono和Flux,这两个类实现接口Publisher,提供了丰富的操作符。Flux对象实现发布者,可以返回N个元素,Mono对象实现发布者,可以放回0或者一个元素
3、Flux和Mono都是数据流的发布者,使用Flux和Mono都可以发送三种数据信号:元素值、错误信息、完成信息,需要知道的是,错误信息和完成信息都是终止信息,终止信息用于告诉订阅者数据流结束了。
4、通过代码进行演示Flux和Mono
4)Webflux执行流程和核心API
SpringWebflux基于Reactor默认使用容器是Netty,Netty是高性能的NIO框架,异步非阻塞的框架
BIO:表示阻塞框架
NIO表示非阻塞框架:
SpringWebFlux执行过程和SpringMVC相似:
SpringWebflux核心控制器DispathHandler,实现接口WebHandler
SpringWebflux中的DispathcherHandler,负责请求的处理:
·HandlerMapping:请求查询到的处理方法
·HandlerAdapter:真正负责请求的处理
·HandlerResultHandler:响应结果处理
5)SpringWebflux基于注解式编程模型
1、创建一个Springboot工程
2、引入相关的依赖
3、配置启动的端口号
4、创建包和相应的实体类等
在Service的实现类的代码为:
package com.test.fluxandmonotest.service.studentServiceImpl;
import com.test.fluxandmonotest.entity.Student;
import com.test.fluxandmonotest.service.StudentService;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
public class StudentServiceImpl implements StudentService {
//通过Map集合模拟从数据库中获取元素
private final Map<String,Student> studentMap=new HashMap<>();
public StudentServiceImpl() {
//将map集合进行初始化
for (int i = 0; i < 3; i++) {
Student student=new Student(""+i,i);
studentMap.put(student.getName(),student);
}
}
@Override
public Mono<Student> getStudentByName(String name) {
//使用Mono.just进行声明元素
return Mono.just(studentMap.get(name));
}
@Override
public Flux<Student> getAllStudent() {
//在Flux中声明一个集合
return Flux.fromIterable(studentMap.values());
}
@Override
public Mono<Void> addStudent(Mono<Student> studentMono) {
return studentMono.doOnNext(student -> {
studentMap.put(student.getName(),student);
}).thenEmpty(Mono.empty()); //通过将Mono中的元素进行清空进行终止
}
}
说明:
SpringMVC方式实现,同步阻塞的方式,基于SpringMVC+Servlet+Tomcat
SpringWebFlux方式实现,异步非阻塞的方式,基于SpringWebFlux+Reactor+Netty
6)SpringWebflux基于函数式编程模型
两个接口:RouterFunction(路由处理)和HandlerFunction(处理函数)
补充:
1、在Spring的配置文件中将properties文件进行引入的方法。
2,bean标签中的scope属性的作用:
3、bean的生命周期:
标签:springframework,bean,import,org,com,public,Spring5 From: https://www.cnblogs.com/just1t/p/16926182.html