Spring 入门
一、Spring详细概括
1、spring是一个开源的轻量级(体积小、jar包少、不需要依赖其他jar)的JavaEE框架
2、spring核心部分IOC、AOP
(1)IOC:创建对象由ioc负责不需要New对象
(2)Aop:不修改源代码就可以实现对功能的添加
3、Spring特点:
(1)方便解耦,简化开发
(2)方便整合框架
(3)降低JavaEE Api的使用难度(例:对JDBC进行封装)
二、IOC底层原理
1、简单使用
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user唯一标识" class="cn.dao.User" /></bean>
实体类:
public void user{
public void add(){
System.out.println("wdnmd");
}
}
测试:
public static void main(String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");//加载配置文件
User us = ctx.getBean("user唯一标识",User.class);//对象创建成功
us.add();
}
2、IOC介绍
控制反转,对象的控制权移交给外部容器。解耦。
IOC通过 XML解析、工厂模式、反射进行管理。
例:(Start调用User的add方法)
1、原始(耦合太高,User中add方法变化,Start调用里要跟着变
而且调用一次new一次)
User us = new User();
us.add();
2、工厂模式(为了解耦而出现)
(1)、创建UserFactory类
public void UserFactory(){
public static User getUser(){
return new User();
}
}
(2)、调用
User use = UserFactory.getUser();
use.add();
3、IOC(最终进化版 XML解析+工厂模式+反射)
(1)、xml文件进行配置
<bean id="唯一标识" class="zz.User"/>
(2)、创建工厂
public void UserFactory(){
public static User getUser(){
String classvalue = class属性值 //1、XML解析。
Class clazz = Class.forName(classValue)//2、反射创建对象。
return (User)clazz.newInstance();
}
}
3、IOC重要接口
1、IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
2、Spring提供两种IOC容器实现方式:(两个接口)
都是加载配置文件==》获取配置创建对象
(1)、BeanFactory:(最基本方式,Spring自带接口,一般Spring内部使用不提供开发人员使用)
(2)、ApplicationContext:(BeanFactory的子接口,提供更多更强大的功能,一般开发人员使用)
区别:
BeanFactory加载配置文件不会去创建对象,获取时才创建对象。
ApplicationContext会把加载配置文件的对象创建。
好处:
放在Tomcat时启动项目时就把耗时耗资源的操作加载出来,BeanFactory不行用的时候才加载
ApplicationContext接口有实现类
(1)、FileSystemXmlApplicationContext:盘符路径
(2)、ClassPathXmlApplicationContext:类路径
4、IOC操作bean管理(xml基本就不用基本都用注解撒)
bean管理具体指的是两个操作创建对象 和注入属性
而且bean管理有两种实现方式基于XML配置文件方式和基于注解方式
(1)、IOC操作bean管理(基于XML实现bean管理)
DI:依赖注入(是IOC的一部分)
1、xml方式创建对象
<bean id="user" class="cn.entity.User"/>
id属性:唯一标识 class:类路径
注:默认也是执行类无参构造方法
2、xml方式注入属性
(1)、有参构造注入(提供有参构造)
<bean id="user" class="cn.entity.User">
//通过定义单参构造方法为userserviceimpl的dao属性赋值
//外部bean注入属性
<constructor-arg><ref bean="引用bean"/></constructor-arg>
<constructor-arg name="name" value="有参构造注入"></constructor-arg>
</bean>
//内部bean太鸡肋有印象就行。
(2)、set注入(提供set方法)
<bean id="user" class="cn.dao.User">
<property name="name" value="WDNMD"></property>
</bean>
(3)、P命名空间注入(需要引入 xmlns:p="http://www.springframework.org/schema/p")
<bean id="user" class="cn.dao.User" p:name="WDNMD"></bean>
注:p:属性名=“属性值” p:属性名-ref=“nean的id”
(4)、注入不同的数据类型
1、简单类型、特殊字符
<property><value><![CDATA[$&#$@##@$]]></value></property>
2、List
<bean id="user" class="entity.User">
<property name="hobbies">
<list><value>1</value><value>2</value>
</list>
</property>
</bean>
3、Map
<bean id="user" class="entity.User">
<property name="hobbies">
<map><entry><key><value>键</value></key><value>值</value></entry>
</map>
</property>
</bean>
4、空字符串注入<value></value> <bean name="user"><null/></bean>
(5)、级联赋值(撕~好多鱼)
方法1:
<bean id="teachers" class="entity.teacher">
<property name="对象(一对多)" ref="引用bean的唯一标识"></property>
</bean>
<bean id="引用bean的唯一标识" class="entity.students">
<property name="student" value="张三"></property>
</bean>
方法2:
<bean id="teacher" class="entity.teacher">
<property name="对象(一对多)" value="引用bean的唯一标识"></property>
//需要提供get方法
<property name="student.student" value="张三"></property>
</bean>
(2)、IOC操作bean管理(Factorybean)
Spring有两种bean
1、普通bean(bean的class定义类型和返回类型必须一致)
2、工厂(Factory为了创建bean过程不被暴漏)bean(bean的class定义类型和返回类型可以一致)
工厂bean使用方法
第一步:创建类,让此类成为工厂类,实现Factory接口即可
第二部:实现接口中的方法,在方法中定义bean的返回类型
public class Mybean implents Factory<随意类型>(){
public 随意类型 getObject()throws Execption{
随意类型 名字 = new 随意类型();
名字.属性="赋值";
return 名字;
}
}
XML中:
<bean id="mybean" class="conf.Mybean"></bean>
测试:
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");//容器初始化
随意类型 名字 = ctx.getBean("mybean",随意类型.class);//对象创建成功
(3)、IOC操作bean管理(bean作用域和生命周期)
作用域:bean默认是单例模式,可以设置成多例模
1、设置单例 || 多例
单例:(默认,加载配置文件时就创建)
<bean id="wdnmd" class="WDNMD" scope="singleton"</bean>
多例:(每次请求创建新的实例bean,getbean获取时才创建)
<bean id="wdnmd" class="WDNMD" scope=""prototype></bean>
2、生命周期
(1)通过构造器创建bean实例(无参构造)
(2)为bean的属性设置值||引用(set方法设置)
(3)调用bean的初始化方法(自己写方法名随意)
xml中:
<bean id="wdnmd" class="entity.WDNMD" init-method="initmethod"></bean>
实体类中:
public void initmethod(){
System.out.Print("bean初始化");
}
(4)获取bean对象
随意类型 名字 = ctx.getBean("mybean",随意类型.class);//对象创建成功
(5)容器关闭,销毁bean(自己写方法名随意)
xml中:
<bean id="wdnmd" class="entity.WDNMD" destory-method="destorymethod"></bean>
实体类中:
public void destorymethod(){
System.out.Print("bean销毁");
}
//测试类中
//手动销毁,销毁后才会调用上面销毁方法
加载配置文件所创建的对象.close();
第三步前后还有一个操作把bean实例传递给后置处理器。
(4)、IOC操作bean管理(bean自动装配)
byName:根据名称自动装配()
<bean id="teacher" class="entity.teachers" autowrie="btName"></bean>
//id必须对应teacher中的student属性名称
<bean id="student" class="entity.student"></bean>
byType:根据类型自动装配
还是别用了太鸡肋了跟上面一样类型一致就行,但是多个bean他就不知道用那个了
(5:重点)、IOC操作bean管理(基于注解实现bean管理)
注解简化xml配置
注:所有注解都是创建bean对象。@MapperScan扫描指定包下注解,想要用注解需要开启扫描,不然Spring不知道做什么
@MapperScan=<context:component-scan base-package=”com.eric.spring”>(xml方式配置)
@component
泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
@component默认值为(value="指定类的名字把首字母小写")
@Service
用于标注业务层组件
@controller
用于标注控制层组件
@Repository
用于标注数据访问组件,即DAO组件
@Configuration
标注此类为配置类
加载配置类:ApplicationContext con = new AnnotationConfigApplicationContext(配置类名字.class);
常用的注解注入
@Autowired:根据属性类型进行注入 @Qualifier:根据属性名称注入
@Resource:可以根据类型也可以根据属性注入 @Value:普通属性注入(标注在属性上)
区别:
多个实现类情况下@Autowired+@Qualifier(value="指定一个实现类首字母小写")
而Resource就牛逼了多个实现类@Resource(name="指定一个实现类首字母小写")
Java提供@Resource Spring提供@Autowired和@Qualifier
三、AOP的底层学习
AOP为面向切面编程(不改变源代码,为项目添加新功能),对业务逻辑各个部分进行隔离,
从而降低耦合度提高可重用性
1、底层原理
AOP底层使用动态代理
1、动态代理有两种情况
(1)、有接口,使用JDK动态代理(创建接口实现类代理实现增强)
//调用Proxy中的newProxyInstance方法(有三个参数)
//参数一:类加载器 参数二:增加类方法的接口(支持多接口) 参数三:增强的部分
1、创建接口
public class Subject(){
public interface history(int age){
return age;
}
}
2、实现接口
public class SubjectImpl implements Subject(){
public int history(int age){
return age;
}
}
3、创建一个代理类JDKProxy(名字按照规范即可)
public class JDKProxy(){
public static void main(String[]args){
//创建接口代实现类代理对象
Class interfaces[] = {Subject.class};
SubjectImpl Subjects = new SubjectImpl();
Subject sub =(Subject)Proxy.newProxyInstance(JDKProxy.class.getClassLoder,interfaces,
new Strong(Subjects));
}
}
4、创建一个类实现InvocationHandler接口
//把创建的是谁的代理对象,就传谁过来
private Object obj;
public Subject(Object obj){
this.obj=obj
}
public class Strong implements InvocationHandler(){
@Override//根据method判断增强那个方法,如果方法a,如果方法b
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//写增强逻辑
//方法执行前
System.out.print("方法名称:"+method.getName()+"传递的参数:"+Arrays.toString(args));
//执行原有方法
Object res = method.invoke(obj,args);
//方法之后
System.out.print(obj);
return res;
}
}
(2)、无接口,使用CGLIB动态代理(创建当前类子类的代理对象)
2、AOP术语
(1)、连接点
类的哪些方法可以被增强,这些方法就叫做连接点
(2)、切入点
实际被真正增强的方法,叫做切入点
(3)、通知(增强)
实际增强的逻辑部分问叫做通知。(有5种类型)
1、前置通知
方法之前做的通知
2、后置通知
方法之后做的通知(有异常就不通知)
3、环绕通知
方法前后都做的通知
4、异常通知
出现异常时做的通知
5、最终通知
不管是否有异常都会通知
(4)、(切面)
把通知应用到切入点的过程叫做切面
3、AOP准备操作
Spring一般基于AspectJ实现AOP操作
AspectJ是Spring的组成部分,独立AOP框架,一般把AspectJ和Spring一起使用进行AOP操作,基于AspectJ实现AOP有两种方式
1、xml方式 2、注解方式
//切入点表达式(为了知道对那个类中那个方法增强)
语法:execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表]))
例: //单个方法 //所有方法直接.*
execution(* 空格 [代表匹配所有修饰符]nb.Dao.UserDao.add(...[代表参数列表]))
1、通过注解实现AOP(AspectJ,要开启注解扫描@MapperScan)
(1)、创建类,再类里定义方法
@Compoment
public class wdnmd(){
public void dnmd(){
System.out.printl("WDNMD");
}
}
(2)、创建增强类并创建方法
@Compoment
@Aspect
public class StrongProxy(){
//前置通知
@Before(execution(* 类路径.需要增强的方法名称(..)))
public void before(){
System.out.printl("前置增强!");
}
}
(3)、配置
<?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
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解扫描,可以扫描com.mq包下的所有bean -->
<context:component-scan base-package="com.mq"/>
<!-- 启动AspectJ支持 -->
<aop:aspectj-autoproxy />
</beans>
(4)、配置不同类型通知
//什么类通知在通知方法上加什么类型注解
//前置通知
@Before(execution(* 类路径.需要增强的方法名称(..)))
//方法后执行 //返回值之后执行 //异常通知
@After(最终通知) @AfterReturning(后置通知) @AfterThrowing
//环绕通知
@Around
(5)、相同切入点抽取
@Pointcut(value="execution(* 空格 [代表匹配所有修饰符]nb.Dao.UserDao.add(...[代表参数列表])")
public void pubs(){
}
//通知就可以简化
@Before(value="pubs()")
(6)、多个增强类对同一个方法进行增强,设置增强优先级
//在代理类加@Order(0-~ 0的优先级最高)实现增强的优先级
四、事务(不包含查询)
事务是数据库操作最进本的单元,逻辑上的一组操作,要么都成功,一个是失败全部失败
1、事务概念
(1)、原子性
要么都成功,一个失败全部失败
(2)、一致性
操作前后总量一致
(3)、隔离性
两人同时间操作同一条数据不会产生影响
(4)、持久性
对数据库中的数据的改变就是永久性的
2、Spring中事务的使用
(1)、开启事务
(2)、逻辑操作
(3)、事务(Spring有两种事务方式)
1、编程式事务管理
try{
//事务开启
//编写业务逻辑
//事务提交
}catch(Execption e){
//事务回滚
}
2、声明式事务管理(又分为两种)
(1)、xml方式(知道有就好。。。。。就不写了。。。。嗯。。。。)
(2)、注解方式
步骤一、在spring配置文件中引入<tx:>命名空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean id="defaultTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 使用annotation定义事务 -->
<tx:annotation-driven transaction-manager="defaultTransactionManager" proxy-target-class="true"/>
</beans>
步骤二、在service的类 || 方法上添加事务注解(@Transactional)
//添加到类上所有方法添加事务,添加方法上单个方法添加事务
3、Spring中事务参数详解
事务参数
属性 | 类型 | 描述 |
---|---|---|
value | String | 可选的限定描述符,指定使用的事务管理器 |
propagation | enum: Propagation | 可选的事务传播行为设置 |
isolation | enum: Isolation | 可选的事务隔离级别设置 |
readOnly | boolean | 读写或只读事务,默认读写 |
timeout | int (in seconds granularity) | 事务超时时间设置 |
rollbackFor | Class对象数组,必须继承自Throwable | 导致事务回滚的异常类数组 |
rollbackForClassName | 类名数组,必须继承自Throwable | 导致事务回滚的异常类名字数组 |
noRollbackFor | Class对象数组,必须继承自Throwable | 不会导致事务回滚的异常类数组 |
noRollbackForClassName | 类名数组,必须继承自Throwable | 不会导致事务回滚的异常类名字数组 |
(1)、传播设置:(propagation )Spring在TransactionDefinition类中定义了以下7种传播特性
PROPAGATION_REQUIRED:如果不存在外层事务,就主动创建事务;否则使用外层事务
PROPAGATION_SUPPORTS:如果不存在外层事务,就不开启事务;否则使用外层事务
PROPAGATION_MANDATORY:如果不存在外层事务,就抛出异常;否则使用外层事务
PROPAGATION_REQUIRES_NEW:总是主动开启事务;如果存在外层事务,就将外层事务挂起
PROPAGATION_NOT_SUPPORTED:总是不开启事务;如果存在外层事务,就将外层事务挂起
PROPAGATION_NEVER:总是不开启事务;如果存在外层事务,则抛出异常
PROPAGATION_NESTED:如果不存在外层事务,就主动创建事务;否则创建嵌套的子事务
示例:@Transactional(propagation=PROPAGATION.REQUIRED)
(2)、隔离级别(isolation 解决并发时出现问题):多事务操作之间不会产生影响
不使用有三个问题:
脏读:读取到了别的事务回滚前的数据,例如B事务修改数据库X,在未提交前A事务读取了X的值,而B事务发生了回滚。
不可重复读:一个事务在两次读取同一个数据的值不一致。例如A事务读取X,在中间过程中B事务修改了X的值,事务A再次
读取X时值发生了改变。
幻读:查询得到的数据条数发生了改变,例如A事务搜索数据时有10条数据,在这时B事务插入了一条数据,A事务再搜索时
发现数据有11条了。
参数可以使用的值:
READ-UNCOMMITTED:未提交读(脏读、不可重复读、幻读)
READ-COMMITTED:已提交读(不可重复读、幻读),大多数主流数据库的默认事务等级,保证了一个事务不会读到
另一个并行事务已修改但未提交的数据,避免了“脏读取”。
REPEATABLE-READ(mysql默认):可重复读(幻读),保证了一个事务不会修改已经由另一个事务读取但未提交
(回滚)的数据。
SERIALIZABLE:串行化最严格的级别,事务串行执行,资源消耗最大
示例:@Transactional(isolation=Isolation.REPEATABLE-READ)
(3)、超时时间(timeout)
//事务在规定时间内提交,否则进行回滚。默认为-1单位秒
示例:@Transactional(timeout="5")
(4)、是否只读(readOnly)
//默认为false,如果为true则只能进行查询操作
示例:@Transactional(readOnly=true || false)
(5)、异常回滚(rollbackFor )
//出现那些异常进行回滚
示例:@Transactional(rollbackFor=Execption)
(6)、异常不回滚(noRollbackFor)
//出现那些异常不进行回滚
示例:@Transactional(norollbackFor=Execption)
迷途者寻影而行
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了