1、Spring 概念
1)Spring框架概述
1、Spring是轻量级的开源的J2EE框架
2、Spring可以解决企业应用开发的复杂性
3、Spring有两个核心部分:IOC和AOP
- IOC:控制反转,把创建对象的过程交给Spring进行管理
- AOP:面向切面,不修改源代码进行功能增强(动态代理的实现)
4、Spring特点:
- 方便解耦,简便开发
- AOP编程的支持
- 声明式的事物支持
- 方便程序测试
- 方便和其他框架进行整合
- 降低API开发难度
5、构成图
2)Spring下载
3)入门案例
1、创建工程
2、导入Spring5的jar包
以及commons-logging的jar包
3、创建一个普通的类
package com.daiju;
/**
* @Author WDY
* @Date 2020-07-07 16:22
* @Description TODO
*/
public class User {
public void add(){
System.out.println("add...............");
}
}
4、创建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.daiju.User"></bean>
</beans>
5、进行代码测试编写
@Test
public void testAdd(){
//1、加载spring配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
//2、获取配置创建对象
User user = context.getBean("user", User.class);
System.out.println(user);
user.add();
}
2、IOC容器
IOC概念
1、什么是IOC
- 控制反转,把对象的创建和对象之间的调用过程都交给Spring进行管理
- 目的是为了降低耦合度
- 入门案例就是IOC的实现
1)IOC底层原理
底层技术:xml解析、工厂模式、反射
1、图解
2)IOC接口
1、IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
2、Spring提供IOC容器实现的两种方式:(两个接口)
1)BeanFactory:IOC容器最基本实现,是Spring内部的使用接口,不提供给开发人员进行使用
BeanFactory加载配置文件时不会创建对象,在获取对象时才去创建对象
2)ApplicationContext :BeanFactory接口的子接口,提供更对更强大的功能,一般由开发人员使用
ApplicationContext加载配置文件的时候就会把在文件中配置的对象进项创建(推荐)
3)ApplicationContext接口实现类
3)IOC操作Bean管理(基于xml)
1、什么是Bean管理
1、Spring创建对象(默认执行无参构造方法)
2、Spring注入属性
2、Bean管理操作的两种方式
1、基于xml配置文件方式实现
2、基于注解方式实现
3、IOC操作Bean管理
1、基于xml方式创建对象
<!--配置User对象创建--> <bean id="user" class="com.daiju.User"></bean>
(1)在Spring配置文件中,使用bean标签,表现里面添加对应的属性,就可以实现对象的创
(2)在bean标签有很多属性,常用的属性如下:
id
获取对象的唯一标识
class
对象的类全路径(包类路径)
2、基于xml方式注入属性
(1)DI:依赖注入,就是注入属性
- set方法注入
1、创建一个类
public class Book { //创建属性 private String name; //创建属性对应的set方法 public void setName(String name) { this.name = name; } }
2、编写配置文件
<!--set方法注入属性--> <bean id="book" class="com.daiju.Book"> <!--使用property完成属性注入--> <!-- name:类里面属性的名称 value:属性的值 --> <property name="name" value="abc"></property> </bean>
3、测试
Book book = context.getBean("book", Book.class); System.out.println(book);
- 有参构造方法注入
1、创建一个类
public class Order { //创建属性 private String name; private String address; //创建有参构造函数 public Order(String name, String address) { this.name = name; this.address = address; } }
2、编写配置文件
<!--有参构造方法注入属性--> <bean id="order" class="com.daiju.Order"> <constructor-arg name="name" value="zhangsan"></constructor-arg> <constructor-arg name="address" value="test"></constructor-arg> </bean>
3、测试
Order order = context.getBean("order", Order.class); System.out.println(order);
3、p名称空间注入(了解)
1、使用p名称空间注入可以简化基于xml配置方式
1、添加p名称空间在配置文件
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
2、进行属性注入,在bean标签里面进行操作
<!--set方法注入属性--> <bean id="book" class="com.daiju.Book" p:name="123"></bean>
4、xml注入其他类型属性
- 字面量
1、null值
<!--在property便签中加入null标签--> <bean id="book" class="com.daiju.Book"> <property name="name"> <null></null> </property> </bean>
2、属性包含特殊符号
<!-- 1、把<>进行转义,<,> 2、把带特殊符号内容写到CDATA --> <bean id="book" class="com.daiju.Book"> <property name="name"> <value><![CDATA[<<南京>>]]></value> </property> </bean>
- 注入属性-外部bean
1、创建两个类Service和Dao
public class UserService { //创建UserDao类型的属性,生成set方法 UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void add(){ System.out.println("service add................."); } }
public interface UserDao { public void update(); }
public class UseeDaoImpl implements UserDao{ @Override public void update() { System.out.println("implements update................."); userDao.update(); } }
2、在xml中配置
<!--1、service和dao对象的创建--> <bean id="service" class="com.service.UserService"> <!-- 注入UserDao对象 name:属性名 ref:创建userDao对象bean的id值 --> <property name="userDao" ref="user"></property> </bean> <bean id="user" class="com.dao.UseeDaoImpl"></bean>
3、在Service中调用Dao中的方法
@Test public void testAdd(){ //1、加载spring配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); UserService service = context.getBean("service", UserService.class); service.add(); }
- 注入属性-内部bean
1、一对多关系:
- 实体类创建
//部门类 public class Dept { private String name; public void setName(String name) { this.name = name; } }
//员工类 public class Emp { private String name; private String gender; //员工属于某一个部门,使用对象形式表示 private Dept dept; public void setDept(Dept dept) { this.dept = dept; } public void setName(String name) { this.name = name; } public void setGender(String gender) { this.gender = gender; } }
- 配置文件编写
<bean id="emp" class="com.bean.Emp"> <property name="name" value="zhangsan"></property> <property name="gender" value="nan"></property> <property name="dept"> <bean id="dep" class="com.bean.Dept"> <property name="name" value="baoan"></property> </bean> </property> </bean>
- 测试
public void testAdd(){ //1、加载spring配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); Emp emp = context.getBean("emp", Emp.class); System.out.println(emp); }
-
注入属性-级联赋值
<bean id="emp" class="com.bean.Emp"> <property name="name" value="zhangsan"></property> <property name="gender" value="nan"></property> <property name="dept" ref="dep"></property> </bean> <bean id="dep" class="com.bean.Dept"> <property name="name" value="baoan"></property> </bean> <!--第二种写法--> <bean id="emp" class="com.bean.Emp"> <property name="name" value="zhangsan"></property> <property name="gender" value="nan"></property> <property name="dept" ref="dep"></property> <property name="dept.name" value="jishubu"></property> </bean> <bean id="dep" class="com.bean.Dept"> <property name="name" value="baoan"></property> </bean>
5、xml注入集合属性
public class Stu {
private String[] course;
private List<String> list;
private Map<String,String> map;
private Set<String> set;
public void setSet(Set<String> set) {
this.set = set;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setCourse(String[] course) {
this.course = course;
}
}
- 注入数组类型属性
<bean id="student" class="com.daiju.Stu"> <property name="course"> <array> <value>java</value> <value>c#</value> <value>python</value> </array> </property> </bean>
- 注入List集合类型属性
<bean id="student" class="com.daiju.Stu"> <property name="list"> <list> <value>张三</value> <value>李四</value> <!--设置对象值--> <ref bean="course"></ref> </list> </property> </bean>
- 注入Set集合类型属性
<bean id="student" class="com.daiju.Stu"> <property name="set"> <set> <value>one</value> <value>two</value> </set> </property> </bean>
- 注入Map集合类型属性
<bean id="student" class="com.daiju.Stu"> <property name="map"> <map> <entry key="1" value="one"></entry> <entry key="2" value="two"></entry> </map> </property> </bean>
- 把集合注入公共部分抽取出来
1、在配置文件中引入名称空间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/util/spring-util.xsd">
2、引入
<!--1提取list集合属性注入--> <util:list id="bookList"> <value>java</value> <value>c#</value> <value>python</value> </util:list> <!--2提取部分的使用--> <bean id="student" class="com.daiju.Stu"> <property name="list" ref="bookList"></property> </bean>
6、FactoryBean
Spring有两种类型的bean,一种普通bean,另外一种工厂bean(FactoryBean)
-
普通bean:返回的类型是配置文件中定义的类型
-
工厂bean:返回的类型可以不是配置文件中定义的类型
1、创建类,让这个类作为工厂bean,实现接口FactoryBean
public class Mybean implements FactoryBean<Stu> { //定义返回bean @Override public Stu getObject() throws Exception { return new Stu(); } @Override public Class<?> getObjectType() { return null; } @Override public boolean isSingleton() { return false; } }
2、实现接口里面的方法,在实现的方法中定义返回的bean类型
<bean id="student" class="com.daiju.factoryBean.Mybean"></bean>
@Test public void te(){ ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); Stu mybean = context.getBean("mybean", Stu.class); }
7、bean作用域
1、在Spring里面设置bean创建的实例是单例还是多例(默认情况下是单例)
2、如何设置是单例还是多例
<bean id="mybean" class="com.daiju.factoryBean.Mybean" scope="singleton"></bean> <!-- scope属性来设置是否是单例还是多例 singleton:默认值,单例,此时加载配置文件时就会创建单例对象 prototype:多例,在加载配置文件的时候不创建对象,在获取对象时才会创建对象 -->
8、bean生命周期
(1)通过构造器创建bean实例(无参构造函数)
(2)为bean的属性设置和对其他bean的引用(调用set方法)
(3)调用bean的初始化的方法(需要进行配置初始化的方法)
(4)bean可以使用了(对象获取到了)
(5)当容器关闭的时候,调用bean的销毁方法(需要进行配置销毁的方法)
演示
- 创建所需的类
/** * @Author WDY * @Date 2020-07-09 14:08 * @Description TODO */ public class Order { private String onname; public Order() { System.out.println("第一步,执行无参构造创建bean实例"); } public void setOnname(String onname) { this.onname = onname; System.out.println("第二步,调用set方法设置属性的值"); } //创建执行初始化的方法 public void initMethod(){ System.out.println("第三步,执行初始化方法"); } //创建执行销毁的方法 public void destroyMethod(){ System.out.println("第五步,执行销毁方法"); } }
- 编辑配置文件
<bean id="order" class="com.daiju.factoryBean.Order" init-method="initMethod" destroy-method="destroyMethod"> <property name="onname" value="test"></property> </bean> <!-- init-method:类中自定义初始化方法的名称 destroy-method:类中自定义销毁方法的名称 -->
- 测试方法
@Test public void te(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); Order order = context.getBean("order", Order.class); System.out.println("第四步,获取到创建bean的实例对象"+order); //手动销毁bean实例 context.close(); }
- 结果
bean后置处理器,bean的生命周期有七步
(1)通过构造器创建bean实例(无参构造函数)
(2)为bean的属性设置和对其他bean的引用(调用set方法)
(3)把bean的实例传递给bean的后置处理器的方法(postProcessBeforeInitialization)
(4)调用bean的初始化的方法(需要进行配置初始化的方法)
(5)把bean的实例传递给bean的后置处理器的方法(postProcessAfterInitialization)
(6)bean可以使用了(对象获取到了)
(7)当容器关闭的时候,调用bean的销毁方法(需要进行配置销毁的方法)
创建后置处理器(实现BeanPostProcessor接口)
import org.springframework.beans.factory.config.BeanPostProcessor; /** * @Author WDY * @Date 2020-07-09 14:32 * @Description TODO */ public class MyBean implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("初始化之前执行的方法"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("初始化之后执行的方法"); return bean; } }
配置文件配置后置处理器(对配置文件中的所有bean都起作用)
<!--配置后置处理器--> <bean id="myBean" class="com.daiju.factoryBean.MyBean"></bean>
结果
9、xml方式自动装配
1、什么是自动装配
1、根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入
package com.daiju.aotuwire; /** * @Author WDY * @Date 2020-07-09 14:45 * @Description TODO */ public class Emp { private Dept dept; public void setDept(Dept dept) { this.dept = dept; } @Override public String toString() { return "Emp{" + "dept=" + dept + '}'; } public void test(){ System.out.println(dept); } }
package com.daiju.aotuwire; /** * @Author WDY * @Date 2020-07-09 14:45 * @Description TODO */ public class Dept { @Override public String toString() { return "Dept{}"; } }
<!--实现自动装配 bean标签的autowire属性,配置自动装配 常用值: byName:根据属性的名称注入,注入值bean的id和类属性名称一样 byType:根据属性的类型注入 --> <bean id="emp" class="com.daiju.aotuwire.Emp" autowire="byName"></bean> <bean id="dept" class="com.daiju.aotuwire.Dept"></bean>
10、外部属性文件
1、创建外部属性文件,properties格式
prop.driverClassName=com.mysql.cj.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/mydb
prop.username=root
prop.password=123456
2、把外部properties配置文件引入到spring配置文件中
1、引入context名称空间
<?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">
2、引入配置文件
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:config.properties"></context:property-placeholder>
3、配置
<!--配置连接池-->
<bean id="druid" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClassName}"></property>
<property name="url" value="${prop.url}"></property>
<property name="username" value="${prop.username}"></property>
<property name="password" value="${prop.password}"></property>
</bean>
4)IOC操作Bean管理(基于注解)
1、什么是注解
1、注解是代码特殊标记,格式:@注解名称(属性名称=属性值…,属性名称=属性值)
2、使用注解,注解作用在类上面,方法上面,属性上面
3、使用注解目的:简化xml文件的配置
2、创建对象的注解
- @Component(普通创建bean注解)
- @Service(一般用在服务层)
- @Controller(一般用在web层)
- @Repository(一般用在持久层)
四个注解的功能是一样的,都可以创建bean实例
3、实现对象的创建
1、引入依赖
2、开启注解扫描
<!-- base-package:指定需要扫描的包中的类 用,分割指定多个包名 其他属性: use-default-filters:表示是否使用默认的filter,自己配置的filter context:include-filter标签,设置扫描那些内容 --> <context:component-scan base-package="com"> <!--表示只扫描这个包下带Controller的注解--> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <!--表示不扫描这个包下带Controller的注解--> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
3、创建类使用注解
/** * @Author WDY * @Date 2020-07-09 14:45 * @Description TODO */ @Component(value = "emp") //value属性指定创建对象时的名字,默认是类名的首字母小写(小驼峰形式) public class Emp { @Autowired//根据属性注入,不需要set方法 private Dept dept; @Override public String toString() { return "Emp{" + "dept=" + dept + '}'; } public void test(){ System.out.println(dept); } }
3、属性注入的注解
- @AutoWired:根据属性类型进行自动装配
- @Qualifier:根据属性名称进行注入
- 需要和@AutoWired配合使用,用value属性指定需要注入的属性名称
- @Resource:可根据类型注入也可根据属性名称注入
- 使用name属性时是根据名称注入
- 不使用时,默认是根据类型注入
- @Value:注入普通属性
- 使用value属性指定注入的属性
4、完全注解开发
1、创建配置类,替代xml配置文件
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @Author WDY
* @Date 2020-07-09 16:29
* @Description TODO
*/
@Configuration //作为配置类,替代xml文件
@ComponentScan(basePackages = {"com.daiju"}) //自定扫描的包
public class config {
}
2、测试类编写
public class test {
@Test
public void te(){
//1、加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(config.class);
//2、获取对象
Order order = context.getBean("order", Order.class);
}
}
3、AOP(Aspect Oriented Programming)
1、什么是AOP
- 面向切面编程:利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
- 通俗来讲就是在不改变原有代码的基础之上,增加新的功能
2、AOP底层原理
1、采用动态代理实现
- 有接口情况,使用JDK动态代理
底层实现原理
1、相关类
2、调用newProxyInstence方法
3、JDK动态代理代码
- 创建接口,定义方法
/** * @Author WDY * @Date 2020-07-10 13:10 * @Description TODO */ public interface UserDao { public int add(int a ,int b); public String update(String id); }
- 创建接口实现类,实现方法
/** * @Author WDY * @Date 2020-07-10 13:11 * @Description TODO */ public class UserDaoImpl implements UserDao{ @Override public int add(int a, int b) { return a+b; } @Override public String update(String id) { return id; } }
- 创建代理对象
//创建代理对象代码 class UserDaoProxy implements InvocationHandler{ //被代理的对象 private Object object; //1、把创建的是谁的代理对象传过来 //有参构造传递 public UserDaoProxy(Object object){ this.object = object; } //增强的逻辑 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //方法之前 System.out.println("方法之前执行"+method.getName()+"------传递的参数"+ Arrays.toString(args)); //原方法 Object res = method.invoke(object,args); //方法之后 System.out.println("方法之后执行"+ object); return res; } }
- 使用Proxy类创建接口代理对象测试
/** * @Author WDY * @Date 2020-07-10 13:14 * @Description TODO */ public class JDKProxy { public static void main(String[] args) { Class[] interfaces = {UserDao.class}; //返回值为代理对象 UserDao userDao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(new UserDaoImpl())); int res = userDao.add(1, 2); System.out.println(res); } }
- 没有接口情况,使用CGLIB动态代理
3、AOP操作中的术语
- 连接点
- 类里面的哪些方法可以被增强,这些方法就称作连接点
- 切入点
- 实际真正被增强的方法,称为切入点
- 通知(增强)
- 实际增强的逻辑部分
- 有多种类型
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知
- 切面(是一个动作)
- 把通知应用到切入点的过程
4、AOP操作
1、Spring框架一般是基于AspectJ实现AOP操作
AspectJ不是Spring的组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作
2、基于AspectJ实现AOP操作
1、基于xml配置文件实现
2、基于注解方式实现
3、在项目中引入AOP依赖
4、切入点表达式
(1)切入点表达式的作用:知道对那个类里面的方法进行增强
(2)语法结构:
execution([权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]))
4-1、AOP操作(注解)
1、创建类
/**
* @Author WDY
* @Date 2020-07-10 14:30
* @Description TODO
*/
public class User {
public void add(){
System.out.println("add..........");
}
}
2、创建增强类(编写增强的逻辑)
在增强类里面创建不同的方法,代表不同的类型通知
public class UserProxy {
//前置通知
public void before(){
System.out.println("before..........");
}
}
3、进行通知的配置
(1)开启注解扫描
<?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.daiju.aop"></context:component-scan>
</beans>
(2)使用注解创建对象
@Component
public class User {
public void add(){
System.out.println("add..........");
}
}
(3)在增强类上面添加注解@Aspect
@Component
@Aspect//生成代理对象
public class UserProxy {
//前置通知
public void before(){
System.out.println("before..........");
}
}
(4)开启生成代理对象
<?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">
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
(5)配置不同类型的通知
//前置通知
@Before(value = "execution(* com.daiju.aop.User.add(..))")
public void before(){
System.out.println("before..........");
}
(6)测试
@Test
public void tesr(){
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
User user = context.getBean("user", User.class);
user.add();
}
(7)其他类型通知
@Component
@Aspect//生成代理对象
public class UserProxy {
//前置通知
@Before(value = "execution(* com.daiju.aop.User.add(..))")
public void before(){
System.out.println("before..........");
}
//最终通知
@After(value = "execution(* com.daiju.aop.User.add(..))")
public void after(){
System.out.println("after..........");
}
//后置通知(返回通知)
@AfterReturning(value = "execution(* com.daiju.aop.User.add(..))")
public void afterReturning(){
System.out.println("afterReturning..........");
}
//异常通知
@AfterThrowing(value = "execution(* com.daiju.aop.User.add(..))")
public void afterThrowing(){
System.out.println("afterThrowing..........");
}
//环绕通知
@Around(value = "execution(* com.daiju.aop.User.add(..))")
public void aroud(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕之前..........");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("环绕之后..........");
}
}
(8)相同切入点抽取
@Component
@Aspect//生成代理对象
public class UserProxy {
@Pointcut(value = "execution(* com.daiju.aop.User.add(..))")
public void pointdemo(){
}
//前置通知
@Before(value = "pointdemo()")
public void before(){
System.out.println("before..........");
}
//最终通知
@After(value = "pointdemo()")
public void after(){
System.out.println("after..........");
}
//后置通知
@AfterReturning(value = "pointdemo()")
public void afterReturning(){
System.out.println("afterReturning..........");
}
//异常通知
@AfterThrowing(value = "pointdemo()")
public void afterThrowing(){
System.out.println("afterThrowing..........");
}
//环绕通知
@Around(value = "pointdemo()")
public void aroud(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕之前..........");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("环绕之后..........");
}
}
(9)有多个增强类对同一个方法进行增强,设置增强类优先级
在增强类上添加@Order注解(数字类型),值越小优先级越高
@Component
@Aspect//生成代理对象
@Order(3)
4-1、AOP操作(xml配置文件)
<bean id="user" class="com.daiju.aop.User"></bean>
<bean id="userProxy" class="com.daiju.aop.UserProxy"></bean>
<!--配置aop增强-->
<aop:config>
<!--切入点-->
<aop:pointcut id="p" expression="execution(* com.daiju.aop.User(..))"/>
<!--配置切面-->
<aop:aspect ref="userProxy">
<!--增强作用在那个方法上面-->
<aop:before method="before" pointcut-ref="p"></aop:before>
</aop:aspect>
</aop:config>
4、JDBCTemplate
1、什么是JDBCTemplate
Spring框架对JDBC进行了封装,从而能够更加简单的实现对数据库的操作
2、导入依赖
3、配置连接池
<context:property-placeholder location="classpath:config.properties"></context:property-placeholder> <context:component-scan base-package="com.daiju"></context:component-scan> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${prop.driverClassName}"></property> <property name="url" value="${prop.url}"></property> <property name="password" value="${prop.password}"></property> <property name="username" value="${prop.username}"></property> </bean>
4、创建JDBCTemplate对象
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
5、创建Service类,dao类,在dao注入JDBCTemplate对象
package com.daiju.service; import com.daiju.dao.BookDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @Author WDY * @Date 2020-07-11 13:28 * @Description TODO */ @Service public class BookService { @Autowired private BookDao bookDao; }
package com.daiju.dao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; /** * @Author WDY * @Date 2020-07-11 13:29 * @Description TODO */ @Repository public class BookDaoImpl implements BookDao{ @Autowired private JdbcTemplate jdbcTemplate; }
- 创建数据表和实体类
public class Book { private Integer user_id; private String username; private String userstatus; @Override public String toString() { return "User{" + "user_id=" + user_id + ", username='" + username + '\'' + ", userstatus='" + userstatus + '\'' + '}'; } public User() { } public User(Integer user_id, String username, String userstatus) { this.user_id = user_id; this.username = username; this.userstatus = userstatus; } public Integer getUser_id() { return user_id; } public void setUser_id(Integer user_id) { this.user_id = user_id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getUserstatus() { return userstatus; } public void setUserstatus(String userstatus) { this.userstatus = userstatus; } }
- 编写service和dao
@Service public class BookService { @Autowired private BookDao bookDao; public void addBook(Book book){ bookDao.addBook(book); } }
@Repository public class BookDaoImpl implements BookDao{ @Autowired private JdbcTemplate jdbcTemplate; @Override public void addBook(Book book){ String sql = "insert into t_book values (?,?,?)"; Object[] args = {book.getUser_id(), book.getUsername(), book.getUserstatus()}; int update = jdbcTemplate.update(sql, args); System.out.println(update); } }
测试
public void tesr(){ ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); BookService bookService = context.getBean("bookService", BookService.class); Book book = new Book(); book.setUser_id(1); book.setUsername("java"); book.setUserstatus("Y"); bookService.addBook(book); }
查询所有操作
//查询表记录数 @Override public void findBook() { String sql = "select count(*) from t_book"; int update = jdbcTemplate.queryForObject(sql,Integer.class); System.out.println(update); } //查询某个对象 @Override public void findBook(Integer id) { String sql = "select * from t_book where user_id=?"; Book update = jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<Book>(Book.class),id); System.out.println(update); } //查询集合 @Override public void findBook() { String sql = "select * from t_book"; List<Book> update = jdbcTemplate.query(sql,new BeanPropertyRowMapper<Book>(Book.class)); System.out.println(update); }
- 批量操作
//批量添加 public void addBook(){ List<Object[]> args = new ArrayList<>(); Object[] obj1 = {"1","C#","Y"}; Object[] obj2 = {"2","Java","Y"}; Object[] obj3 = {"3","Js","Y"}; args.add(obj1); args.add(obj2); args.add(obj3); String sql = "insert into t_book values (?,?,?)"; int[] update = jdbcTemplate.batchUpdate(sql, args); System.out.println(update); }
5、事务管理
1、什么是事务
事务是数据库操作的最基本单元,逻辑上的一组操作,要么都成功,要么都失败(转账)
2、事务四大特性(ACID)
- 原子性
- 一致性
- 隔离性
- 持久性
3、搭建操作环境
- 创建数据表
- 创建相应的实体类
@Service public class UserService { @Autowired private UserDao userDao; }
public interface UserDao { }
@Repository public class UserDaoImpl implements UserDao{ @Autowired private JdbcTemplate jdbcTemplate; }
- 在dao创建多钱和少钱的方法,在service创建转账的方法
//多钱 public void addMoney(); //少钱 public void reduceMoney();
@Override public void addMoney() { String sql = "update account set money=money+? where id=?"; jdbcTemplate.update(sql,100,1); } @Override public void reduceMoney() { String sql = "update account set money=money-? where id=?"; jdbcTemplate.update(sql,100,2); }
//转账的方法 public void account(){ userDao.addMoney(); userDao.reduceMoney(); }
4、Spring事务管理介绍
1、事务添加到JavaEE三层架构中的Service层
2、在Spring中进行事务管理操作有两种方式
- 编程式事务管理(会造成代码臃肿)
- 声明式事务管理(推荐使用)
3、声明式事务管理
- 基于注解
- 基于xml配置文件
4、在Spring中进行声明式事务管理,底层使用AOP原理
5、Spring事务管理API
- 提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类
-
5、基于注解实现
1、创建事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--注入数据源--> <property name="dataSource" ref="dataSource"></property> </bean>
2、开启事务注解
引入名称空间tx
<?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" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--开启事务注解 transaction-manager:指定创建的事务管理器 --> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
3、在service类上面(或者service类里面的方法上面)添加事务注解==@Transactional==
@Service @Transactional public class UserService { .... }
4、配置==@Transactional==的参数
propagation:事务传播行为
多事务方法1之间进行调用,这个过程中事务是如何进行管理的
isolation:事务隔离级别
- 事务有特性称为隔离性,多事务操作之间不会产生影响,不考虑隔离性会产生一系列问题
- 脏读:一个未提交的事务读取到另一个未提交事务的数据
- 幻(虚)读:一个未提交的事务读取到另一个提交事务添加的数据
- 不可重复读:一个未提交的事务读取到另一个提交事务修改的数据
timeout:超时时间
- 事务需要在一定的时间内进行提交,超时则不提交进行回滚操作
- 默认值-1,单位:秒
readOnly:是否只读
- 默认是false
rollbackFor:回滚
- 设置出现哪些异常进行事务的回滚
noRollbackFor:不回滚
- 设置出现哪些异常不进行事务的回滚
6、基于xml配置文件实现
1、配置事务管理器(详情5-1)
2、配置通知
<!--配置通知--> <tx:advice id="txAdvice"> <!--配置事务参数--> <tx:attributes> <!--指定在哪种方法上面添加事务--> <tx:method name="account" isolation="DEFAULT"/> </tx:attributes> </tx:advice>
3、配置切入点和切面
<!--配置切入点和切面--> <aop:config> <!--配置切入点--> <aop:pointcut id="pt" expression="execution(* com.daiju.service.UserService.*(..))"/> <!--配置切面--> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor> </aop:config>
6、Spring5新特性
1、整个框架基于java8,运行时兼容jdk9,许多不建议使用的类和方法在代码库中进行了删除
2、Spring5框架自带了通用的日志封装
1、Spring5已经移除了Log4jConfigListener,官方建议使用Log4j2(Logback)
2、Spring5整合Log4j2
- 引入jar包
- 创建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==注解
1、该注解可以使用在方法上面,属性上面,参数上面,表示方法返回值可以为空,属性值可以为空,参数值可以为空
4、Spring5核心容器支持函数式风格GenericApplicationContext
@Test //函数式风格创建对象,交给spring管理 public void test(){ //1、创建GenericApplicationContext对象 GenericApplicationContext applicationContext = new GenericApplicationContext(); //2、调用applicationContext的方法进行对象注册 applicationContext.refresh(); applicationContext.registerBean("user",User.class,()->new User()); //3、获取在spring注册的对象 User bean = applicationContext.getBean("user",User.class); }
5、支持整合JUnit5
1、整合JUnit4
- 引入spring针对测试相关的依赖
- 创建测试类,使用注解方式实现测试
@RunWith(SpringJUnit4ClassRunner.class)//单元测试框架 @ContextConfiguration("classpath:application.xml")//加载配置文件 public class Test { public void tesr(){ .......... } }
2、整合JUnit5
- 引入JUnit5的jar包
- 创建测试类,使用注解方式实现测试
@ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:application.xml") //@SpringJUnitConfig(locations = "classpath:application.xml")可以替代上面两个注解 public class Test1 { @Test public void tesr(){ ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); } }
6、SpringWebFlux
6.1、介绍
1、是Spring5添加新的模块,用于web开发的,功能和SpringMVC类似的,WebFlux使用当前一种比较流行响应式编程出现的框架
2、使用传统的web框架,比如SpringMVC,这些是基于Servlet容器,WebFlux是一种异步非阻塞的框架,异步非阻塞框架在Servlet3.1以后才支持,WebFlux核心是基于Reactor的相关API实现的
3、特点:
- 非阻塞可以在有限资源情况下,提高系统的吞吐量和伸缩性,以Reactor为基础实现响应式编程
- 函数式编程,WebFlux使用jdk8函数式编程方式实现路由请求
4、比较SpringMVC
- 都可以使用注解方式,也可以运行在Tomcat等容器中
- SpringMVC采用命令式编程,WebFlux采用异步响应式编程
6.2、响应式编程
1、什么是响应式编程
响应式编程式一种面向数据流和变化传播的编程范式。这意味着可以再编程语言中很方便的表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播
2、java8及之前版本
提供的观察者模式的两个类Observer和Observable
public class ObserverDemo extends Observable { public static void main(String[] args) { ObserverDemo observerDemo = new ObserverDemo(); //添加观察者 observerDemo.addObserver((o,arg)->{ System.out.println("发生了变化"); }); observerDemo.addObserver((o,arg)->{ System.out.println("手动被观察者通知,准备改变"); }); //数据变化 observerDemo.setChanged(); //通知 observerDemo.notifyObservers(); } }
3、响应式编程(Reactor实现)
1、响应式编程操作中,Reactor是满足Reactive规范的框架
2、Reactor有两个核心类,Mono和Flux,这两个类实现接口Publisher,提供丰富操作符。Flux对象实现发布者,返回N个元素;Mono实现发布者,返回0或1个元素
3、Flux和Mono都是数据流的发布者,使用Flux和Mono都可以发出三种数据信号,元素值,错误信号,完成信号,错误信号和完成信号都代表终止信号,终止信号用于告诉订阅者数据流结束了,错误信号终止数据流同时把错误信息传递给订阅者
4、代码演示Flux和Mono
- 引入依赖
<dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-core</artifactId> <version>3.3.3.RELEASE</version> </dependency>
- 编写代码
public class TestReactor { public static void main(String[] args){ //just方法直接声明 Flux.just(1,2,3,4).subscribe(System.out::println);//订阅 Mono.just(1); //其他方法 Integer[] array = {1,2,3,4}; Flux.fromArray(array); List<Integer> list = Arrays.asList(1,2,3,4); Flux.fromIterable(list); Stream<Integer> stream = list.stream(); Flux.fromStream(stream); } }
5、三种信号特点
- 错误信号和完成信号都是终止信号,不能共存
- 如果没有发送任何元素值,而是直接发送完成信号或者错误信号,表示空数据流
- 如果没有完成信号,也没有错误信号,表示无限数据流
6、调用just或者其他方法只是声明数据流,数据流并没有发出,只有订阅后才会触发数据流
7、操作符
- 对数据流进行一道道的操作,称为操作符,比如流水线
- map:元素映射为新的元素
- flatmap:元素映射为流
- 把每个元素转换成流,把转换后的多个流合并成一个大的流
6.3、执行流程和核心API
SpringWebFlux是基于Reactor,默认容器是Netty,Netty是高性能的NIO框架,异步非阻塞的框架
1、Netty
- BIO(阻塞)
- NIO(非阻塞)
2、SpringWebFlux执行过程和SpringMVC相似
- SpringWebFlux核心控制器DispatchHandler,实现接口WebHandler
3、SpringWebFlux里面的DispatchHandler负责请求的处理
- HandlerMapping:请求查询到的处理的方法
- HandlerAdapter:真正负责请求处理
- HandlerResultHandler:响应结果处理
4、SpringWebFlux实现函数式编程,两个接口
- RouterFunction:路由
- HandlerFunction:处理函数
6.4、基于注解编程模型
1、导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> <version>2.2.5.RELEASE</version> </dependency>
2、配置启动端口号
server.port=8081
3、创建相关的类和接口
package com.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * @Author WDY * @Date 2020-07-12 16:27 * @Description TODO */ @Data @AllArgsConstructor @NoArgsConstructor public class User { private String name; private String gender; private Integer age; }
/** * @Author WDY * @Date 2020-07-12 16:29 * @Description TODO */ //用户操作接口 public interface UserService { //根据id查询用户 Mono<User> getUserById(Integer id); //查询所有用户 Flux<User> getAllUser(); //添加用户 Mono<Void> saveUserInfo(Mono<User> user); }
@Repository public class UserServiceImpl implements UserService { //创建map集合存储数据 private final Map<Integer, User> users = new HashMap<>(); public UserServiceImpl(){ this.users.put(1, new User("Lucy", "男", 20)); this.users.put(2, new User("Marry", "女", 30)); this.users.put(3, new User("Tom", "男", 22)); this.users.put(4, new User("Jack", "女", 25)); this.users.put(5, new User("Dacy", "男", 29)); } @Override public Mono<User> getUserById(Integer id) { return Mono.justOrEmpty(this.users.get(id)); } @Override public Flux<User> getAllUser() { return Flux.fromIterable(this.users.values()); } @Override public Mono<Void> saveUserInfo(Mono<User> user) { return user.doOnNext(person->{ int i = users.size()+ 1; users.put(i, person); }).thenEmpty(Mono.empty()); } }
@RestController public class UserController { @Autowired private UserService userService; @GetMapping("/user/{id}") public Mono<User> getUserById(@PathVariable(name = "id") int id){ return userService.getUserById(id); } @GetMapping("/user") public Flux<User> getAllUser(){ return userService.getAllUser(); } @PostMapping("/saveuser") public Mono<Void> saveUser(@RequestBody User user){ Mono<User> userMono = Mono.just(user); return userService.saveUserInfo(userMono); } }
- SpringMVC方式实现,同步阻塞的方式,基于SpringMVC+Tomcat
- SpringWebFlux方式实现,异步非阻塞方式,基于SpringWebFlux+Reactor+Netty
6.5、基于函数是编程模型
1、需要我们自己初始化服务器
2、两个核心接口:RouterFunction和HandlerFunction
- RouterFunction:实现路由功能,请求转发给相应的Handler
- HandlerFunction:处理请求生成响应的函数,核心任务定义两个函数式接口的实现并启动需要的服务器
3、SpringWebFlux请求和响应不再是ServletRequest和ServletResponse,而是ServerRequest和ServerResponse
1、创建Handler
public class UserHandler { private final UserService userService; public UserHandler(UserService userService) { this.userService = userService; } //根据id查询 public Mono<ServerResponse> getUserById(ServerRequest request){ //获取id Integer id = Integer.valueOf(request.pathVariable("id")); //空值处理 Mono<ServerResponse> notFound = ServerResponse.notFound().build(); //调用service方法 Mono<User> userMono = this.userService.getUserById(id); //把userMono进行转换返回 //使用Reactor操作flatMap return userMono .flatMap(user -> ServerResponse .ok() .contentType(MediaType.APPLICATION_JSON) .body(fromObject(user))) .switchIfEmpty(notFound); } //查询所有 public Mono<ServerResponse> getAllUser(ServerRequest request){ Flux<User> allUser = this.userService.getAllUser(); return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(allUser, User.class); } //添加 public Mono<ServerResponse> saveUser(ServerRequest request){ //得到user对象 Mono<User> userMono = request.bodyToMono(User.class); return ServerResponse.ok().build(this.userService.saveUserInfo(userMono)); } }
2、初始化服务器,编写Router
public class Server { //1创建路由 public RouterFunction<ServerResponse> routerFunction(){ UserService userService = new UserServiceImpl(); //创建Handler对象 UserHandler handler = new UserHandler(userService); //设置路由 return RouterFunctions.route( GET("/user/{id}").and(accept(MediaType.APPLICATION_JSON)),handler::getUserById) .andRoute(GET("/user").and(accept(MediaType.APPLICATION_JSON)),handler::getAllUser); } }
3、创建服务器完成适配
//2创建服务器完成适配 public void createReactorServer(){ //路由和Handler适配 RouterFunction<ServerResponse>route = routerFunction(); HttpHandler httpHandler = toHttpHandler(route); ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler); //创建服务器 HttpServer httpServer = HttpServer.create(); httpServer.handle(adapter).bindNow(); }
4、最终调用
public static void main(String[] args) throws Exception{ Server server = new Server(); server.createReactorServer(); System.out.println("enter to exit"); System.in.read(); }
5、完整代码
public class Server { //1创建路由 public RouterFunction<ServerResponse> routerFunction(){ UserService userService = new UserServiceImpl(); //创建Handler对象 UserHandler handler = new UserHandler(userService); //设置路由 return RouterFunctions.route( GET("/user/{id}").and(accept(MediaType.APPLICATION_JSON)),handler::getUserById) .andRoute(GET("/user").and(accept(MediaType.APPLICATION_JSON)),handler::getAllUser); } //2创建服务器完成适配 public void createReactorServer(){ //路由和Handler适配 RouterFunction<ServerResponse>route = routerFunction(); HttpHandler httpHandler = toHttpHandler(route); ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler); //创建服务器 HttpServer httpServer = HttpServer.create(); httpServer.handle(adapter).bindNow(); } public static void main(String[] args) throws Exception{ Server server = new Server(); server.createReactorServer(); System.out.println("enter to exit"); System.in.read(); } }
6、使用WebClient调用
public class test { public static void main(String[] args) { //调用服务器地址 WebClient client = WebClient.create("http://localhost:54906"); //请求路径 String id = "1"; User block = client.get().uri("/user/{id}", id) .accept(MediaType.APPLICATION_JSON) .retrieve() .bodyToMono(User.class) .block(); System.out.println(block); Flux<User> users = client.get().uri("/user") .accept(MediaType.APPLICATION_JSON) .retrieve() .bodyToFlux(User.class); users.buffer().doOnNext(System.out::println).blockFirst(); } }
事务方法是对数据库表数据进行变化的操作 ↩︎