Spring
Spring
1. Spring
组成--七大模块
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.11.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.11.RELEASE</version>
</dependency>
扩展
- Spring Boot
- 一个快速开发的脚手架
- 基于SpringBoot可以快速开发单个微服务
- 约定大于配置
- Spring Cloud
- SpringCloud是基于SpringBoot实现的
2. IOC理论
控制反转
程序员不用再管理对象的创建了,系统的耦合性大大降低。可以更加专注在业务的实现。
IOC : 控制反转
DI : 依赖注入,控制反转的一种实现方式
- 反转:程序本身不创建对象,编程被动的接收对象
- 依赖注入 : 利用set方法进行注入
- 对象是由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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="Hello" class="com.Rickduck.pojo.Hello">
<property name="str" value="Spring" />
</bean>
</beans> IOC创建对象的方式
无参构造方式创建
<bean id="user" class="com.Rickduck.pojo.User" name="u1,u2"> </bean>
有参构造方式
下标赋值
<!-- 第一种,下标赋值--> <bean id="user" class="com.Rickduck.pojo.User"> <constructor-arg index="0" value="index-ref" /> </bean>通过类型创建
<!-- 第二种,通过类型创建,不建议使用--> <bean id="user" class="com.Rickduck.pojo.User"> <constructor-arg type="java.lang.String" value="type-ref" /> </bean>直接通过参数名
<bean id="user" class="com.Rickduck.pojo.User"> <property name="user" value="Spring"> </bean>
在加载配置文件的时候,容器中管理的对象就已经初始化了!!!
3. Spring配置
3.1 别名
<!-- 配置别名 --> <alias name="name" alias="newName"></alias>
3.2 Bean的配置
<!--id:唯一标识 class:bean对象对应的全限定名 name:别名(可以取多个别名)--> <bean id="user" class="com.Rickduck.pojo.User" name="u1,u2"> </bean>
3.3 import
一般用于团队开发,可以将多个配置文件导入合并为一个。
<import resource="beans.xml" />
4. 依赖注入
4.1 构造器注入
4.2 Set方式注入
- 依赖注入:Set注入
- 依赖:bean对象的创建依赖于容器!
- 注入:bean对象钟的所有属性都由容器来注入!
8种类型注入:
<bean id="student" class="com.Rickduck.pojo.Student">
<!-- 1.普通值注入-->
<property name="name" value="Rickduck" />
<!-- 2.Bean注入-->
<property name="address" ref="address" />
<!-- 3.数组注入-->
<property name="books">
<array>
<value>水浒传</value>
<value>红楼梦</value>
<value>三国演义</value>
<value>西游记</value>
</array>
</property>
<!-- 4.List注入-->
<property name="hobbys">
<list>
<value>歌曲</value>
<value>代码</value>
<value>篮球</value>
</list>
</property>
<!-- 5.Map注入-->
<property name="card">
<map>
<entry key="ID" value="123456" />
<entry key="key" value="value" />
</map>
</property>
<!-- 6.Set注入-->
<property name="games">
<set>
<value>LOL</value>
<value>QQ飞车</value>
</set>
</property>
<!-- 7.空值注入-->
<property name="wife">
<null />
</property>
<!-- 8.Properties注入-->
<property name="info">
<props>
<prop key="学号">123456</prop>
<prop key="姓名">2333</prop>
</props>
</property>
</bean> 4.3 扩展方式注入
p命名空间方式注入
c命名空间方式注入
注意点 :p命名、c命名空间不能直接使用,需要导入xml约束
4.4 Bean的作用域
单例模式(默认)
- 每次从容器中get取得时候都是同一个对象,只有一个实例对象
原型模式
- 每次从容器中get的时候都会产生一个对象
其余得request、session、application 这些只在web中使用
5. Bean的自动装配
- 自动装配是Spring满足bean依赖得一种方式!
- Spring会在上下文中自动寻找,并自动给bean装配属性
三种Spring装配方式
- xml中显示配置
- java中显示配置
- 隐式的自动装配bean(重要)
<bean id="cat" class="com.Rickduck.pojo.Cat"></bean>
<bean id="dog" class="com.Rickduck.pojo.Dog"></bean>
<!--
byName:自动在容器上下文和自己对象set方法值对应的beanid
byType:自动在容器上下文和自己对象属性类型相同的bean(保证全局唯一)
-->
<bean id="person" class="com.Rickduck.pojo.Person" autowire="byName">
<property name="name" value="Rickduck"></property>
</bean> - byName:保证beanid唯一,并且和需要注入属性set方法的值一致
- byType:保证bean的class唯一
使用注解实现自动装配
jdk1.5支持注解
spring2.5支持注解
步骤:
- 导入约束:context约束
- 配置注解的支持:context:annotation-config/
<?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> @Autowired
- 直接在属性上使用
- 可不使用set方法
- 默认byType
@Qualifier
- 可指定属性
- value = "name"
@Resouce
- 默认byName
6.使用注解开发
1.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/>
<!-- 开启包扫描 -->
<context:component-scan base-package="com.Rickduck"/>
</beans> 2.属性注入
- @Component
- 组件,用在pojo类上,
@Component
public class User{
public String name;
//相当于applicationContext.xml中配置Bean时的属性注入
@value("Rickduck")
public void setName(String name){
this.name = name;
}
} 3.衍生的注解
@Component有几个衍生注解,在web开发中会按照mvc三层架构分层!
- dao : @Repository
- service : @Service
- controller : @Controller
4. 自动装配
- @Autowired : 自动装配通过类型。名字
如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value="xxx")
- @Nullable : 说明该字段可以为null
- @Resource : 自动装配通过名字。类型 5.作用域
@Component
@Scope("prototype")
public class User{
public String name;
//相当于applicationContext.xml中配置Bean时的属性注入
@value("Rickduck")
public void setName(String name){
this.name = name;
}
} 6. 小结
xml与注解
- xml更加万能,适用于任何场合!
- 注解不是自己的类不能用,维护相对复杂!
7.使用Java方式配置Spring
JavaConfig是Spring一个子项目,在Spring4之后成为一个核心功能
实体类
@Component
public class User{
private String name;
public String getName(){
return name;
}
@value("Rickduck") //属性值注入
public void setName(String name){
this.name = name;
}
} 配置类:
@Configuration
@ComponentScan("com.Rickduck.pojo")
@Import(Rickduck2.class)
public class RickduckConfig{
// 注册一个bean,相当于xml中的bean标签
// 方法名 : bean的id
// 方法返回值 :bean的class属性
@Bean
public User getUser(){
return new User();
}
} 测试类
public class MyTest{
public static void main(String[] args){
//使用了配置类方式,只能通过AnnotationConfig上下文来获取容器
ApplicationContext context = new AnnotationConfigApplicationContext(RickduckConfig.class);
User getUser = (User)context.getBean("getBean");
System.out.println(getUser.getName());
}
}
纯Java的配置方式,在SpringBoot中随处可见
8. 代理模式
SpringAOP的底层是代理模式
代理模式的分类:
- 静态代理
- 动态代理
8.1 静态代理
角色分析
- 抽象角色 :一般会使用接口或者抽象类来解决
- 真实角色 : 被代理的角色
- 代理角色 :代理真实角色(代理真实角色后一般会做一些附属操作)
- 客户:访问代理对象的人
步骤:
接口
//租房 public interface Rent{ public void rent(); }真实角色
//房东 public class Host implements Rent{ public void rent(){ System.out.println("房东租房!"); } }代理角色
public class Proxy implements Rent{ private Host host; public Proxy(){} public Proxy(Host host){ this.host = host; } public void rent(){ seeHouse(); host.rent(); contract(); fare(); } //看房 public void seeHouse(){ System.out.println("中介带看房!"); } //合同 public void contract(){ System.out.println("签租赁合同!"); } //收取中介费 public void fare(){ System.out.println("收中介费!"); } }客户端访问代理角色
public class Client{ public static void main(String[] args){ //房东要租房子 Host host = new Host(); //代理,中介帮房东出租房子;中间会加上一些附属操作 Proxy proxy = new Proxy(host); //客户不需要面对房东,著需要面对中介 proxy.rent(); } }
代理模式的好处:
- 可以使真实角色的操作更纯粹,不用去关注一些公共的业务
- 公共也就交给了代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色,开发效率变低
8.2 动态代理
底层是反射
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的:基于接口、基于类的动态代理
- 动态代理分为两大类
- 基于接口 -- JDK动态代理
- 基于类 -- cglib
- java字节码实现:javasist
两个类 : Proxy代理、InvocationHandler:调用处理程序
Proxy : 提供创建动态代理类和实例的静态方法
InvocationHandler:调用处理程序并返回结果
InvocationHandler
public class ProxyInvocationHandler implements InvocationHandler{
//被代理的接口
private Object target;
public void setTarget(Object target){
this.target = target;
}
//生成得到代理类
//类加载器,代理接口,处理程序
public Object getProxy(){
return Proxy.newProxtInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces,this);
}
//处理代理实例,并返回结果
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
log(method.getName());
Object result = method.invoke(target,args);
return result;
}
public void log(String msg){
System.out.println("执行了"+msg+"方法");
}
}
测试类
public class Clent{
public static void main(String[] args){
//真实角色
UserServiceImpl userService = new UserServiceImpl();
//代理对象通过处理程序动态生成
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//设置需要代理的对象
pih.setTarget(userService);
UserService proxy = (UserService)pih.getProxy();
proxy.query();
}
} 9. AOP
SpringAOP中,通过Adivice定义横切逻辑,Spring中支持五种类型的Advice:
9.1 Spring实现AOP
依赖导入:
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
<scope>runtime</scope>
</dependency>
方式一:Spring的API接口
AOP配置:
需要增加aop的约束!
<?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.Rickduck.service.UserServiceImpl" />
<bean id="log" class="com.Rickduck.log.Log" />
<bean id="afterlog" class="com.Rickduck.log.AfterLog" />
<!-- 配置aop:需要导入aop的约束 -->
<aop:config>
<!-- 切入点: expression:表达式,execution(执行的位置! * * *)-->
<aop:pointcut id="pointcut" expression="execution(* com.Rickduck.service.UserServiceImpl.*(..))"/>
<!-- 执行环绕增加 -->
<aop:advisor advice-ref="log" pointcut-ref="pointcut" />
<aop:advisor advice-ref="afterlog" pointcut-ref="pointcut" />
</aop:config>
</beans> 前置通知
public class Log implements MethodBeforeAdvice {
/*
* method : 要执行的目标对象的方法
* args : 参数
* target : 目标对象
*/
@Override
public void before(Method method,Object[] args,Object target) throws Throwable{
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
后置通知
public class AfterLog implements AfterReturningAdvice{
//returnValue : 返回值
@Override
public void afterReturning(Object returnValue,Method method,Object[] args,Object target) throws Throwable{
System.out.println("执行了"+target.getClass().getName()+"方法,返回结果为:"+returnValue);
}
} 测试类
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
UserService userService = context.getBean("userService",UserService.class);
userService.add();
}
}
方式二 自定义切入点类
自定义切面类
public class DiyPointCut {
public void before(){
System.out.println("方法执行前");
}
public void after(){
System.out.println("方法执行后");
}
}
aop配置
<?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.Rickduck.service.UserServiceImpl" />
<bean id="diy" class="com.Rickduck.diy.DiyPointCut" />
<aop:config>
<!-- 自定义切面 -->
<aop:aspect ref="diy">
<!-- 切入点 -->
<aop:pointcut id="point" expression="execution(* com.Rickduck.service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="point" />
<aop:after method="after" pointcut-ref="point" />
</aop:aspect>
</aop:config>
</beans> 方式三:注解实现AOP
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class AnnotationPointCut {
@Before("execution(* com.Rickduck.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("方法执行前");
}
@After("execution(* com.Rickduck.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("方法执行后");
}
//在环绕增强中
@Around("execution(* com.Rickduck.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
//执行方法
Object proceed = jp.proceed();
System.out.println("环绕后");
}
}
<!-- 注册切面类bean --> <bean id="annotationPointCut" class="com.Rickduck.diy.AnnotationPointCut" /> <!-- 开启注解支持 默认JDK实现--> <aop:aspectj-autoproxy />
10. 整合Mybatis
10.1 Mybatis搭建:
步骤:
导入相关jar包
- junit
- mybatis
- mysql数据库
- spring相关的
- aop织入
- mybatis-spring
编写配置文件
测试
10.2 Mybatis-spring
编写数据源配置
sqlSessionFactory
sqlSessionTemplate
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- DataSource : 使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/mybatics?useSSL=false&useUnicode=true&characterEncoding=UTF-8" /> <property name="username" value="root"/> <property name="password" value="888888"/> </bean> <!-- sqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <!-- 绑定Mybatis配置 --> <property name="configLocation" value="classpath:mybatis-config.xml" /> <property name="mapperLocations" value="classpath:com/Rickduck/mapper/*.xml" /> </bean> <!-- SqlSessionTemplate:就是我们使用的sqlSession--> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!-- 只能使用构造器注入sqlSessionFactory,因为没有set方法--> <constructor-arg index="0" ref="sqlSessionFactory" /> </bean> <bean id="userMapper" class="com.Rickduck.mapper.UserMapperImpl"> <property name="sqlSession" ref="sqlSession" /> </bean> </beans>需要给接口加实现类
测试
public class MyTest {
//mybatis
@Test
public void test() throws IOException {
String resource = "mybatis-config.xml";
InputStream in = Resources.getResourceAsStream(resource);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = sessionFactory.openSession(true);
UserMapper user = sqlSession.getMapper(UserMapper.class);
List<User> userList = user.selectUser();
for (User user1 : userList) {
System.out.println(user1);
}
}
//Spring整合mybatis后
@Test
public void test2() throws IOException{
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
UserMapper userMapper = context.getBean("userMapper",UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
}
实现二:
通过实现类继承SqlSessionDaoSupport
import com.Rickduck.pojo.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
public List<User> selectUser() {
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
}
11. 声明式事务
11.1 事务
- 把一组业务当成一个业务做:要么都成功要么都失败
- 设计数据一致性
- 确保完整性和一致性
事务ACID原则:
- 原子性
- 要么都成功要么都失败
- 一致性
- 事务执行前后数据库处于一致的状态
- 隔离性
- 并发事务是相互隔离的,防止数据损坏
- 持久性
- 事务一旦提交,无论系统发生什么问题结构都不在被影响,被持久化到数据库中
11.2 spring中的事务管理
- 声明式事务:AOP
- 编程式事务:需要在代码中对事务进行管理
事务的传播级别:
在applicationContext中进行事务的配置
<!-- 配置声明式事务 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 结合AOP实现事务的织入 -->
<!-- 配置事务通知: -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 给哪些方法配置事务 -->
<!-- 配置事物的传播特性: new propagation-->
<tx:attributes>
<tx:method name="add" />
<tx:method name="delete" />
<tx:method name="update" />
<tx:method name="query" read-only="true"/>
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<!-- 配置事务切入 -->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.Rickduck.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
</aop:config>
为什么需要事务:
- 如果不配置事务,可能存在数据提交不一致的情况
- 如果不在Spring中配置声明式事务,就需要在代码中手动配置事务
- 事务十分重要!!!
记录生活
查看17道真题和解析