很粗略,回顾的时候会进行大幅度修改
一.概念
1.前提
在前面maven及mybatis学习中,我们每次去新添加一个类,也要在service里面去实现修改,很繁琐,那么就会引进一些东西使其变得简单。恰好Spring里面恰好有这个功能
2.概念
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)
3.组成
3.1特点
-
分层架构,七个模块
-
Spring模块在核心容器之上
-
核心容器定义了创建、配置和管理bean的方式
不懂不着急,我们后面解释
3.2相关概念解释
-
核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
-
Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
-
Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
-
Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
-
Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
-
Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
-
Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI
除此之外,Springboot是Spring的脚手架
二.IOC
1.前提
在maven里面修改userdao类还要修改service中的实现类,而如果工作量大,这个就显得效率低下,那么,怎么办
2.方法
需要它的地方,我们留一个接口,利用set修改,像这样
private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; }
-
之前,程序是主动创建对象!控制权在我们手上
-
使用了set注入后,程序不再具有主动性,而是变成了被动的接收对象
-
我们不用再管理对象的创建
3.本质
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。 控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)
4.创建对象方式
使用无参构造创建对象,默认! 假设我们要使用有参构造创建对象。
<!--第一种方式:下标赋值 --> <bean id="user" class="com.cyyehh.pojo.User"> <constructor-arg index="0" value="测试"/> </bean> 类型 <!--第二种方式:通过类型的创建,不建议使用 --> <bean id="user" class="com.cyyehh.pojo.User"> <constructor-arg type="java.lang.String" value="lifa"/> </bean> <!--第三种方式:直接通过参数名来设置 --> <bean id="user" class="com.cyyehh.pojo.User"> <constructor-arg name="name" value="测试"/> </bean>
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了
三.Spring配置
1.存在别名
<!--别名,如果添加了别名,我们也可以使用别名获取到这个对象--> <alias name="user" alias="userNew"/>
2.Bean的配置
<!-- id:bean的唯一标识符,也就是相当于我们学的对象名 class:bean对象所对应的全限定名:包名+类名 name:也是别名,而且name可以同时取多个别名 --> <bean id="userT" class="com.cyyehh.pojo.UserT" name="user2 u2,u3;u4"> <property name="name" value="呀哈哈"/> </bean>
3.import
import一般用于团队开发使用,它可以将多个配置文件,导入合并为一个。 假设,现在项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个
<import resource="bean.xml"/> <import resource="bean2.xml"/> <import resource="bean3.xml"/>
四.依赖注入
1.构造器注入
就是ioc创建对象方式,不累述
2.Set方式注入(特别重要)
-
依赖:bean对象的创建依赖于容器
-
注入:bean对象中的所有属性,由容器来注入
搭建环境,省略
完善
<bean id="address" class="com.kuang.pojo.Address"> <property name="address" value="西安"/> </bean> <bean id="student" class="com.kuang.pojo.Student"> <!--第一种:普通值注入,value --> <property name="name" value="黑心白莲"/> <!--第二种: --> <property name="address" ref="address"/> <!--数组 --> <property name="books"> <array> <value>红楼梦</value> <value>西游记</value> <value>水浒传</value> <value>三国演义</value> </array> </property> <!--List --> <property name="hobbies"> <list> <value>打篮球</value> <value>看电影</value> <value>敲代码</value> </list> </property> <!--Map --> <property name="card"> <map> <entry key="身份证" value="123456789987456321"/> <entry key="银行卡" value="359419496419481649"/> </map> </property> <!--Set --> <property name="games"> <set> <value>LOL</value> <value>COC</value> <value>BOB</value> </set> </property> <!--NULL --> <property name="wife"> <null/> </property> <!--Properties --> <property name="info"> <props> <prop key="driver">20191029</prop> <prop key="url">102.0913.524.4585</prop> <prop key="user">黑心白莲</prop> <prop key="password">123456</prop> </props> </property> </bean>
3.拓展方式注入
按c命名空间和p命名空间
<!--p命名空间注入,可以直接注入属性的值:property--> <bean id="user" class="com.kuang.pojo.User" p:name="黑心白莲" p:age="20"/> <!--c命名空间注入,通过构造器注入:constructor-args--> <bean id="user2" class="com.kuang.pojo.User" c:name="cyyehh" c:age="22"/> </beans>
4.bean作用域
1.单例
<bean id="user2" class="com.cyyehh.pojo.User" c:name="cyyehh" c:age="22" scope="singleton"/>
2.原型:每次从容器中get的时候,都会产生一个新对象
<bean id="user2" class="com.cyyehh.pojo.User" c:name="cyyehh" c:age="22" scope="prototype"/>
五.Bean的自动装配
spring结合上下文去补充完整bean
在Spring中有三种装配的方式:
-
在xml中显式的配置
-
在java中显式配置
-
隐式的自动装配bean
<bean id="cat" class="com.cyyehh.pojo.Cat"/> <bean id="dog" class="com.cyyehh.pojo.Dog"/> <bean id="people" class="com.cyyehh.pojo.People"> <property name="name" value="福运"/> <property name="cat" ref="cat"/> <property name="dog" ref="dog"/> </bean>
下面是装配
<!-- byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean id --> <bean id="people" class="com.cyyehh.pojo.People" autowire="byName"> <property name="name" value="福运"/> </bean>
<!--byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean--> <bean id="people" class="com.kuang.pojo.People" autowire="byType"> <property name="name" value="福运"/> </bean>
ByName:需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
ByType:需要保证所有bean的class唯一,并且这个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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--开启注解的支持 --> <context:annotation-config/> </beans>
六.代理模式
1.静态代理
-
抽象角色:一般会使用接口或者抽象类来解决
-
真实角色:被代理的角色
-
代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
-
客户:访问代理对象的人
//接口 public interface Rent { public void rent(); }
//真实角色 public class UserServiceImpl implements UserService{ public void add() { System.out.println("增加了一个用户!"); } public void delete() { System.out.println("删除了一个用户!"); } public void update() { System.out.println("修改了一个用户!"); } public void query() { System.out.println("查询了一个用户!"); } }
//代理角色 public class UserServiceProxy implements UserService{ private UserServiceImpl userService; public void setUserService(UserServiceImpl userService) { this.userService = userService; } public void add() { log("add"); userService.add(); } public void delete() { log("delete"); userService.delete(); } public void update() { log("update"); userService.update(); } public void query() { log("query"); userService.query(); } public void log(String msg){ System.out.println("[Debug] 使用了一个"+msg+"方法"); } }
//客户端访问代理角色 public class Client { public static void main(String[] args) { UserServiceImpl userService = new UserServiceImpl(); UserServiceProxy proxy = new UserServiceProxy(); proxy.setUserService(userService); proxy.delete(); } }
2.动态代理
前面步骤和静态类似
//我们会用这个类,自动生成代理类! public class ProxyInvocationHandler implements InvocationHandler { //被代理的接口 private Rent rent; public void setRent(Rent rent) { this.rent = rent; } //生成得到代理类 public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(),this); } //处理代理实例,并返回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //动态代理的本质,就是使用反射机制实现! Object result = method.invoke(rent, args); seeHose(); fee(); return result; } public void seeHose(){ System.out.println("中介带着看房子!"); } public void fee(){ System.out.println("中介收取费用!"); } }
七.AOP
1.概念
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
2.作用
提供声明式事务;允许用户自定义切面
横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等… 切面(ASPECT):横切关注点被模块化的特殊对象。即,它是一个类 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法 目标(Target):被通知对象 代理(Proxy):向目标对象应用通知之后创建的对象 切入点(PointCut):切面通知执行的“地点”的定义 连接点(JointPoint):与切入点匹配的执行点
3.实现
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency>
public interface UserService { public void add(); public void delete(); public void update(); public void select(); }
public class UserServiceImpl implements UserService { public void add() { System.out.println("增加了一个用户!"); } public void delete() { System.out.println("删除了一个用户!"); } public void update() { System.out.println("更新了一个用户!"); } public void select() { System.out.println("查询了一个用户!"); } }
public class Log implements MethodBeforeAdvice { //method: 要执行的目标对象的方法 //args:参数 //target:目标对象 public void before(Method method, Object[] agrs, Object target) throws Throwable { System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了"); } }
public class AfterLog implements AfterReturningAdvice { //returnValue: 返回值 public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue); } }
<?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--注册bean--> <bean id="userService" class="com.kuang.service.UserServiceImpl"/> <bean id="log" class="com.kuang.log.Log"/> <bean id="afterLog" class="com.kuang.log.AfterLog"/> <!--方式一:使用原生Spring API接口--> <!--配置aop:需要导入aop的约束--> <aop:config> <!--切入点:expression:表达式,execution(要执行的位置!* * * * *)--> <aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/> <!--执行环绕增加!--> <aop:advisor advice-ref="log" pointcut-ref="pointcut"/> <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/> </aop:config> </beans>
八.声明式事务
1.回顾事务
8.1.1原则
原子性(atomicity) 事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用 一致性(consistency) 一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中 隔离性(isolation) 可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏 持久性(durability) 事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中
2.spring中事务管理
编程式事务管理
将事务管理代码嵌到业务方法中来控制事务的提交和回滚 缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码 声明式事务管理
一般情况下比编程式事务好用 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理,将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理
xmlns:tx="http://www.springframework.org/schema/tx" http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">//约束导入
<!--结合AOP实现事务的织入--> <!--配置事务通知--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!--给那些方法配置事务--> <!--配置事务的传播特性: new --> <tx:attributes> <tx:method name="add" propagation="REQUIRED"/> <tx:method name="delete" propagation="REQUIRED"/> <tx:method name="update" propagation="REQUIRED"/> <tx:method name="query" read-only="true"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice>
在mybatis里面结合Spring省略了