Spring5 简述

IOC 容器

IOC 底层原理

IOC : 控制反转,对象创建和对象之间的调用过程,交给Spring进行管理,达到降低耦合度的目的。

底层原理xml 解析+工厂模式+反射(本质就是对象工厂)

IOC 过程 :

  1. 配置 xml 文件,配置创建的对象

    
    
  2. 有 A 类和 B 类,现需要在 B 中调用 A 中的方法,需创建工厂类

    class XxxFactory{public static A getA(){String classValue = 获取-class属性值; //xml解析(获取类路径)Class clazz = Class.forName(className);//通过反射创建对象return (A)clazz.newInstance();}
    }
    
IOC-BeanFactory 接口

​ **Spring 提供的 IOC 容器两种实现方式 : **

  1. BeanFactory : IOC 容器的基本实现方式,是 Spring 内部使用接口,不提供给开发人员使用。加载配置文件时不会创建对象,在获取或使用对象时才去创建

  2. ApplicationContext : BeanFactory 的子接口,提供更多的功能,面向开发人员使用,加载配置文件时会对配置文件中的对象进行创建

    ApplicationContext 的两个主要实现类

    1.ClassPathXmlApplicationContext2.FileSystemXmlApplicationContext
    
IOC Bean 的管理

Bean 管理的两个操作 : (1) Spring 创建对象 (2) Spring 注入属性

xml 方式
创建对象
  • Spring 配置文件中,使用 bean 标签,然后添加对应属性,就可实现对象的创建
  • bean 标签-常用属性
属性
id唯一标识
class包类路径
  • 创建对象时,默认执行无参构造方法完成对象的创建
注入属性

DI : 依赖注入,(就是注入属性)

  1. 使用 set 方法进行注入

    创建类,定义属性,创建对应的 set 方法

    //name:属性名,value:注入的值
    <bean id="user" class="com.lte.User"><property name="username" value="admin">property><property name="password" value="123">property>
    bean>
    
  2. 使用有参构造器进行注入

    创建类,定义属性,创建对应有参构造方法

    <bean id="user" class="com.lte.User"><constructor-arg name="username" value="liu">constructor-arg><constructor-arg name="password" value="666">constructor-arg>//或者
    bean>
    
  3. p 名称空间注入(略过…,,只是 set 方式的简化写法)

  4. xml 注入其它类型属性

    字面量

    null

    <property name="username"><null/>
    property>
    

    属性值包含特殊符号

    1.2.
    <property name="username"><value>>……^!]]>value>
    property>
    

    注入属性-外部 Bean

    (1) 两个类 service 类和 dao 类 (2)在 service 调用 dao 里面的方法 (3) spring配置文件中进行配置

    
    
    <bean id="userService" class="com.lte.Service.impl.UserServiceImpl"><property name="userDAO" ref = "userDAOImpl">property>
    bean>
    <bean id="userDAOImpl" class="com.lte.DAO.Impl.UserDAOImpl">bean>
    

    -注入属性-内部 Bean 和级联赋值

    创建了 Employee 类 和 Employee 类,Employee 类的其中一个属性为 Department 类型

    
    <bean id="emp" class="com.lte.domain.Employee"><property name="name" value="admin">property><property name="age" value="123">property><property name="dep"><bean id="dep" class="com.lte.domain.Department"><property name="dname" value="财务部">property>bean>property>
    bean>
    

    级联赋值

    
    <bean id="emp" class="com.lte.domain.Employee"><property name="name" value="admin">property><property name="age" value="123">property><property name="dep" ref="dep">property><property name="dep.dname" value="技术部">property>
    bean>
    <bean id="dep" class="com.lte.domain.Department"><property name="dname" value="">property>
    bean>
    

    数组 ,List ,Map ,Set 集合类型的注入

    
    <property name="courses"><array><value>value><value>value>array>
    property>
    
    <property name="lists"><list><value>value><value>value>list>
    property>
    
    <property name="maps"><map><entry key="" value="">entry><entry key="" value="">entry>map>
    property>
    
    <property name="sets"><set><value>value><value>value>set>
    property>
    

    集合类型为对象

    <bean id="" class=""><property name="ObjectList"><list><ref bean="object1">ref><ref bean="object2">ref>list>property>
    bean>
    <bean id="object1" class="xxx.xx"><property name="" value="">property>
    bean>
    <bean id="object2" class="yyy.yy"><property name="" value="">property>
    bean>
    

    提取公共的集合注入部分( list 为例)

    spring 配置文件中引入名称空间(在 beans 标签里设置)
    xmlns:util="http://www.springframework.org/schema/util"
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsdutil标签完成集合的注入提取
    aaaabbbb
    注入使用
    
    
FactoryBean

Spring 的两种类型 bean : (1) 普通 bean ,(2)工厂bean(FactoryBean)

​ 普通 bean :在配置文件中定义的 bean 类型,返回值为相同类型

​ 工厂 bean :配置文件中定义的 bean 类型可以和返回类型不一样

	//自定义的类实现 FactoryBean 接口 ,重写里面的方法//配置文件,创建对象public class FactoryBeanImpl implements FactoryBean 
Bean 的作用域

​ 创建的 Bean 实例 : (1)单实例(默认) ,(2)多实例


<bean id="user" class="com.lte.User" scope="prototype"> 
bean>
Bean 的生命周期
  1. 创建 bean 实例

  2. 注入属性(设置属性值)

  3. 调用 bean 的初始化方法

    init-method="初始化需要执行的方法名(不需要括号)"
    
  4. 使用 bean (获取对象)

  5. 销毁 bean (容器关闭时,调用 bean 的销毁方法)

    destroy-method="销毁 bean 之后需要执行的方法名(不需要括号)"
    

在 bean 的后置处理器的情况下,生命周期有七步

​ 在 3.调用 bean 的初始化方法 之前,bean 的实例传递给 bean 后置处理器的 postProcessBeforeInitialization 方法

​ 在 3.调用 bean 的初始化方法 之后,bean 的实例传递给 bean 后置处理器的 postProcessAfterInitialization 方法

//实现 BeanPostProcessor 接口,重写两个方法
public class MyBean implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("初始化前需要执行的方式");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("初始化之后需要执行的方法");return bean;}
}//配置后置处理器
//会对所在配置文件中的所有 bean 添加后置的处理
<bean id="myBean" class="com.lte.MyBean"></bean>
自动装配

根据指定的装配规则,自动将匹配的属性值进行注入


<bean id="emp" class="com.lte.domain.Employee" autowire="byName"><property name="name" value="admin">property><property name="age" value="123">property><property name="list" ref="lists">property>
bean>

<bean id="dep" class="com.lte.domain.Department"><property name="dname" value="财务部">property>
bean>
引入外部属性文件
//引入 druid 数据库连接池的配置文件为例spring 配置文件中引入名称空间(在 beans 标签里设置)
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd//引入外部属性文件的标签

 //${} : spring 中的表达式,填写内容为数据库配置文件中等号左边的属性名称......

注解方式

​ spring 中针对 Bean 管理中创建对象提供的四种注解,都可以用来创建 bean 实例

  • @Component
  • @Service
  • @Controller
  • @Repository
创建对象
  1. 引入相关依赖 (aop-jar)

  2. 开启组件扫描

    引入名称空间
    xmlns:context="http://www.springframework.org/schema/context"
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd开启组件扫描 
    如果要扫描多个包,包之间用逗号隔开 或 直接扫描上层目录
    
    

    关于组件扫描的两个常见示例

    示例一

    
    <context:component-scan base-package="com.lte.domain" use-default-filters="false"><context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    context:component-scan>

    示例二

    
    <context:component-scan base-package="com.lte.domain"><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    context:component-scan>
  3. 在创建的类上添加注解(注解的 value 如果没有赋值,默认为该类的名称并且首字母小写)

属性注入
  1. @Autowired : 根据属性类型

     //在属性上边使用注解@Autowiredprivate UserService service;
    
  2. @Qualifier : 根据属性名称

     //需要配合  @Autowired 使用@Autowired//value : 对应类的注解的 value 值//并且不需要设置 set 方法@Qualifier(value = "userServiceImpl")private UserService service;
    
  3. @Resource : 根据属性类型 或 名称

      @Resource : 根据类型@Resource(name = "xx") : 根据名称(Resource 是 import javax.annotation.Resource 下的,不属于 spring)
    
  4. @Value : 注入普通类型(以上三种主要用于对象类型)

    @Value(value="admin")
    private String name;
    

完全注解开发 —>(springboot)

AOP 面向切面编程

面向切面编程降低业务逻辑的各个部分之间的耦合度

AOP 底层原理

AOP 底层使用动态代理,其中包含两种情况的动态代理 :

​ 1. 有接口情况 :使用 JDK 中的动态代理 (创建接口实现类的代理对象)

​ 2. 无接口情况 : 使用 CGLIB 动态代理 (创建子类的代理对象)

JDK 提供的动态代理

java.lang.reflect.Proxy 下提供的 newProxyInstance 方法 可以返回指定接口的代理类的实例

newProxyInstance 方法提供的三个参数 :

​ 1.类加载器

​ 2.被代理类所实现的接口,传入的参数为 Class 类型的数组

​ 3. 实现 InvocationHandler 接口,创建代理对象,添加增强的方法

演示

1.创建 UserService 接口,UserServiceImpl 为该接口实现类(被代理)

2.实现 InvocationHandler 接口

//代理类
class UserServiceProxy implements InvocationHandler{private Object obj; //被代理的对象public UserServiceProxy(Object obj) {this.obj = obj;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//方法增强System.out.println(method.getName() + "方法执行前...");Object invoke = method.invoke(obj, args);System.out.println(method.getName() + "方法执行后...");return invoke;}
}

3.调用 newProxyInstance 方法实现代理

//所在的类为 DynamicProxy
Class[] interfaces = {UserService.class};
UserServiceImpl userService = new UserServiceImpl();
UserService userService1 = (UserService) Proxy.newProxyInstance(DynamicProxy.class.getClassLoader(), interfaces, new UserServiceProxy(userService));
AOP 常见操作术语
  1. 连接点

    ​ 类中的哪些方法可以被增强,这些方法称为连接点

  2. 切入点

    ​ 实际被增强的方法,称为切入点

  3. 通知(或增强)

    ​ 实际增加的逻辑部分称为通知 或者 增强

    类型 :前置 ,后置 ,环绕 , 异常 , 最终通知

  4. 切面

    把通知应用到切入点的过程,可以看成一个动作

AOP 操作

spring 框架一般基于 AspectJ 实现 AOP 操作,AspectJ 不是 spring 的组成部分,一般和spring 框架一起使用。

基于 AspectJ 实现 AOP 操作 :(1) xml 配置文件实现 (2) 注解实现

切入点表达式 :明确对某个类中的某个方法进行增强

语法结构execution( [权限修饰符 ( * :任意修饰符) ] [返回类型 (无返回类型可以省略) ] [类全路径 (* :包下的所有类) ] [方法名称(* :该类下的所有方法) ] [ 参数列表 (用两个点表示参数) ] )

AspectJ 注解
  1. 创建 User(被增强类) 类和 UserProxy 类(增强类)

  2. 使用注解创建 User 和 UserProxy 对象

    @Component
    public class User { ... }@Component
    public class UserProxy{ ... }
    
  3. 在增强类上面添加注解 @Aspect

    @Component
    @Aspect
    public class UserProxy{ ... }
    
  4. spring 配置文件中开启组件扫描 和 开启生成代理对象

    
    xmlns:context="http://www.springframework.org/schema/context"
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    
    
    
    xmlns:aop="http://www.springframework.org/schema/aop"
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
    
    
    
  5. 配置不同的通知

    @Component
    @Aspect
    public class UserProxy {// User 类中的 show() 方法作为切入点//前置通知(因为在方法前执行,所有无论有没有异常都会执行)@Before(value = "execution(* com.lte.aopanno.User.show(..))")public void before(){System.out.println("before...");}//最终通知(无论有没有异常都执行)@After(value = "execution(* com.lte.aopanno.User.show(..))")public void after(){System.out.println("after...");}//后置通知(不出现异常,并且在方法返回结果之后执行)@AfterReturning(value = "execution(* com.lte.aopanno.User.show(..))")public void afterReturning(){System.out.println("afterReturning...");}//异常通知(出现异常时执行)@AfterThrowing(value = "execution(* com.lte.aopanno.User.show(..))")public void afterThrowing(){System.out.println("afterThrowing...");}//环绕通知(出现异常时不执行)@Around(value = "execution(* com.lte.aopanno.User.show(..))")public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("环绕之前...");//表示被增强的方法执行proceedingJoinPoint.proceed();System.out.println("环绕之后");}
    }
    

    抽取公共切入点

    //使用 @Pointcut 对相同切入点进行抽取
    @Pointcut(value = "execution(* com.lte.aopanno.User.show(..))")
    public void publicpiont(){}
    //将方法名赋给value
    @Before(value = "publicpiont()")
    public void before(){System.out.println("before...");}
    

    优先级 :

    多个不同的增强类,对同一个方法进行增强,设置增强类优先级

    //通过注解 @Order 设置优先级 ,值越小优先级越高
    @Order(3)
    public class UserProxy{ ... }
    

使用 JdbcTemplate 操作数据库

Spring 框架对 JDBC 进行封装,使用 JdbcTemplate 实现对数据库的操作

操作示例
  1. 导入相关依赖 和 druid 连接池的配置文件

    #druid 部分配置文件
    jdbc.driverClassName=com.mysql.cj.jdbc.Driver
    jdbc.url=jdbc:mysql://127.0.0.1:3306/android?rewriteBatchedStatements = true&serverTimezone=UTC
    jdbc.username=root
    jdbc.password=123123
    
  2. 在 spring 配置文件中 对数据库连接池进行配置

    
    
    <context:property-placeholder location="classpath:druid.properties"/>
    
    
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="${jdbc.driverClassName}">property><property name="url" value="${jdbc.url}">property><property name="username" value="${jdbc.username}">property><property name="password" value="${jdbc.password}">property>
    bean>
    <context:component-scan base-package="com.lte">context:component-scan>
    
  3. 配置 JdbcTemplate对象,注入DataSource

    
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource">property>
    bean>
    
  4. 创建 service 类和 dao 类,在 dao 类中注入 JdbcTemplate 对象

    //UserDAO 的实现类
    @Repository(value = "UserDAOImpl")
    public class UserDAOImpl implements UserDAO {//注入JDBCTemplate@Autowiredprivate JdbcTemplate jdbcTemplate;
    }
    //UserService 的实现类
    @Service(value = "UserServiceImpl")
    public class UserServiceImpl implements UserService {//注入DAO@Qualifier(value = "UserDAOImpl")private UserDAO userDAO;
    }
  5. 操作数据库

    插入数据

    //传入的两个参数 sql语句 和 可变形参
    @Override
    public void add(User user) {String sql = "insert into users values(?,?,?)";Object[] obj = {user.getId(),user.getUsername(),user.getPassword()};int row = jdbcTemplate.update(sql, obj);System.out.println(row);
    }

    删除 和 修改操作也是通过调用 update 方法进行操作

    查询数据

    @Override
    public int findCount() {String sql = "select count(*) from users";Integer count = jdbcTemplate.queryForObject(sql, Integer.class);return count;
    }
    
    @Override
    public User findOne(String id) {String sql = "select * from users where id =?";return jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<User>(User.class),id);
    }
    
    @Override
    public List<User> findAll() {String sql = "select * from users";return jdbcTemplate.query(sql,new BeanPropertyRowMapper<User>(User.class));
    }
    
获取连接时出现的异常

异常 :org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLException: Access denied for user ‘86152’@‘localhost’ (using password: YES)

Caused by: java.sql.SQLException: Access denied for user ‘86152’@‘localhost’ (using password: YES)

原因 :properties 文件 username 被系统环境变量替代了,导致无法连接成功(不是 86152’@‘localhost 而应该是 root’@'localhost)

解决方法:

  1. 修改 username 为 user 或其他名称 (例如 :username 修改为 jdbc.username )

  2. spring导入properties 文件的标签中添加属性:system-properties-mode=“FALLBACK” 不加时属性值默认为 NVIRONMENT

    // 练习时用到的配置标签

批量操作
@Override
public int[] batchAddUser(List<Object[]> lists) {/*   String sql = "update users set username = ?,password = ? where id = ?";*/String sql = "insert into users values(?,?,?)";//batchUpdate : 可以进行批量 插入,修改,删除int[] ints = jdbcTemplate.batchUpdate(sql,lists);return ints;
}

事务

数据库操作的最基本单元,逻辑上的一组操作,有一个失败,整个操作都会失败

事务的四个特性(ACID) : (1)原子性 (2)一致性 (3)隔离性 (4)持久性

利用 spring 进行事务管理
编程式事务声明

​ (一般不用,略…)

声明式事务管理(注解方式)

spring 中进行声明式事务管理,底层使用 AOP 原理

使用事务-示例

  1. 引入 tx 名称空间

  2. 创建事务管理器

    
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource">property>
    bean>
    
  3. 开启事务注解

    
    <tx:annotation-driven transaction-manager="transactionManager">tx:annotation-driven>
    
  4. 添加事务注解

    @Transactional
    添加到类上,类中的所有方法都会添加注解
    添加到方法上,只为当前方法添加事务
    

@Transactional 常用参数

属性名表示
propagation事务传播行为
isolation事务隔离级别
timeout超时时间
readOnly是否只读
rollbackFor回滚
noRollbackFor不回滚

事务传播行为( propagation ) :多事务方法直接进行调用,这个过程中事务是如何进行管理的

@Transactional(propagation = Propagation.REQUIRED)

事务方法 :增 删 改等对数据库表数据进行变化的操作

事务传播行为 :

事务隔离级别( isolation ) : **解决 脏读,不可重复读,幻读 这三个问题 **

	1. **脏读 :当一个事务读取另一个事务尚未提交的修改时,产生脏读**2. **不可重复读 :同一查询在同一事务中多次进行,由其它提交事务所做的修改或删除,每次返回不同的结果集,此时发生不可重复读**3. **幻读 : 同一查询在同一事务中多次进行,由于其他提交事务所作的**插入**操作,每次返回不同的结果集,此时发生幻读。**
//一共存在四种隔离级别
//Isolation.REPEATABLE_READ : 可重复读,不加锁 ,一般为 mysql 默认级别
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)

超时时间( timeout ) : 事务需要在一定时间内进行提交,超出时间还未提交则进行回滚

​ 默认值 :-1 (表示不超时) , 设置时间以 秒 为单位

timeout = -1 //不超时
timeout = 10 //超时时间10秒

是否只读 ( readOnly ):设置成 true 后,只能进行查询(读) ,不能进行增删改(写)

readOnly = true //默认 false

**回滚 ( rollbackFor ) : 设置出现哪些异常进行事务回滚 **

rollbackFor = NullPointerException.class

不回滚 ( noRollbackFor ) : 设置出现的哪些属性不进行回滚

noRollbackFor = NullPointerException.class
声明式事务管理(xml 方式)
  1. 配置事务管理器

    
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource">property>
    bean>
    
  2. 配置通知

    
    <tx:advice id = "txadvice"><tx:attributes><tx:method name="transfer" propagation="REQUIRED"/>tx:attributes>
    tx:advice>
    
  3. 配置切入点和切面

    
    <aop:config><aop:pointcut id="pt" expression="execution(* com.lte.service.AccountService.*(..))"/><aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
    aop:config>
    
声明式事务管理(完全注解)

(省略------,尚硅谷 spring p-49)

其它一些特性

@Nullable 注解

可以使用在 类 \方法\属性\参数\上面,表示方法\属性值\参数值\返回可以为空

WebFlux

**Spring WebFlux :功能和 SpringMVC 类似,但使用的是响应式编程框架,相对于传统框架(例如 SpringMVC), WebFlux 是一种 异步非阻塞框架(该种框架在 Servlet 3.1 以后才支持的),核心是基于 Reactor 相关 API 实现的。 **

特点 :非阻塞式 ,在有限资源下,提高系统吞吐量和伸缩性,使用函数式编程实现路由请求。默认使用容器是 Netty,Netty 为高性能 NIO 异步非阻塞框架
请添加图片描述
*WebFlux 其它知识学习中~~~~*


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部