Java后端07(Spring)
Spring
涉及的设计模式:单例模式,简单工厂模式,代理模式,观察者模式,反射,注解。。。。。
Spring配置文件文件头
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
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">
</beans>
IOC 控制反转 ---- 将创建对象的权力由开发者交 给 Spring(缓解对象和对象之间的耦合度)
在传统模式下,对象的创建和赋值,都是由开发者自己手动完成,事实情况下,开发者只关心如何获取赋值好的对象,但是并不希望自己手动进行创建对象和赋值的事情(spring中所有的对象都是从实例工厂中自动创建,涉及到简单工厂模式)
IOC底层原理
- xml解析(负责读取配置文件中 Bean 的相关信息)
- 简单工厂模式(借助 BeanFactory 完成对象的实例化和返回)
- 反射(不使用 new 创建对象的根本原因)
简单工厂模式
优点:
- 设计与实现分离(开发者不需要关心对象是如何创建出来的)
- 如果要拓展业务,并不会影响之前的业务,只需要拓展子类即可
IOC容器的两种实现方式
- BeanFactory:是Spring内部使用的接口,不建议给开发人员使用,该容器加载配置文件时,并不会创建响应对象,当开发者尝试获取对象的时候,才会创建对象(类似懒汉模式,具有延迟加载的特性)
- ApplicationContext:BeanFactory的子接口,一般由开发人员使用,该容器在加载配置文件的时候,就会创建对象(类似饿汉模式,不会出现并发安全问题,线程安全)
DI 依赖注入 ---- 在你配置好 bean 的情况下,由 Spring 帮你完成对象的赋值
优点:
- 实现了接口和实现的分离
- 组件化的思想,分离关注点,使用接口时,不再关注实现
Spring 的 bean 管理
⭐Bean的生命周期
-
常规五步生命周期
通过构造器创建 bean 实例 => 通过反射调用 setter 方法完成属性赋值 => 调用 bean 的初始化方法 => 获取 bean 实例 => 当容器关闭的时候, 调用 bean 的销毁方法
-
完整七步生命周期
通过构造器创建 bean 实例 => 通过反射调用 setter 方法完成属性赋值 => 在 bean 初始化之前调用后置处理器 => 调用 bean 的初始化方法 => 在 bean初始化之后调用后置处理器 => 获取 bean 实例 => 当容器关闭的时候, 调用 bean 的销毁方法
// 后置处理器 Demo public class MyPostProcessor 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 的 name 或者 type 实现自动装配
- byName 根据属性名称自动注入(注入的 bean 名称要跟属性名称保持一致)
- byType 根据属性的类型自动注入
<!-- byName 方式 自动装配 bean-->
<!-- person 对象再实例化的时候,需要注入 dog 属性,而 dog 属性对应的应该是一个 Dog 类对象的内存地址-->
<!-- byName 会根据你的成员变量名称,找到匹配的 id 的 bean-->
<bean id="person" class="com.iweb.entity.Person" autowire="byName" />
<bean id="dog" class="com.iweb.entity.Dog" />
<!-- byType 方式 根据成员变量的类型,自动装配 id 不需要和成员变量匹配-->
<bean id="person" class="com.iweb.entity.Person" autowire="byType" />
<bean id="dog" class="com.iweb.entity.Dog" />
<!-- 导入外部配置文件-->
<context:property-placeholder location="db.properties" />
<!-- 在 spring 中加载外部配置文件 使用${}传参 以 druid 连接池为例-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
bean创建(IOC)
-
基于xml的配置方式
结构分析
<bean id="person" class="com.iweb.entity.Person" scope="prototype"/>
id
对象的唯一标识,通过 id 获取 bean 实例class
对象所属类的完整路径,用于提供给 spring 借助反射完成的对象创建scope
表示单/多实例模式<!-- 属性注入--> <bean id="dog" class="com.iweb.entity.Dog"> <property name="name" value="dog01"/> <property name="type" value="dogType01"/> </bean> <!-- 构造注入,利用构造函数实现注入 未解决,爆红!!!!!!!!--> <bean id="dog" class="com.iweb.entity.Dog"> <constructor-arg name="name" value="dog01"/> </bean> <!-- 空值注入,手动注入空值--> <bean id="dog" class="com.iweb.entity.Dog"> <property name="name"><null/></property> </bean> <!-- 特殊符号注入--> <bean id="dog" class="com.iweb.entity.Dog"> <property name="name"> <value><![CDATA[(-.-)]]></value> </property> </bean> <!-- 外部 bean 注入(引用绑定)--> <bean id="dog" class="com.iweb.entity.Dog"> <property name="name" value="dog01" /> <property name="type" value="type01" /> </bean> <bean id="person" class="com.iweb.entity.Person"> <property name="dog" ref="dog" /> </bean> <!-- 内部 bean 注入(只能在某一个 bean 的内部访问它,其他 bean 无法访问内部bean)--> <bean id="person" class="com.iweb.entity.Person"> <property name="dog"> <bean class="com.iweb.entity.Dog"> <property name="name" value="dog01" /> <property name="type" value="type01" /> </bean> </property> </bean> <!-- 级联赋值--> <bean id="person" class="com.iweb.entity.Person"> <property name="dog" ref="dog" /> <property name="dog.type" value="type01" /> <property name="dog.name" value="dog01" /> </bean> <bean id="dog" class="com.iweb.entity.Dog" /> <!-- 集合注入 => 数组, list, map, set--> <bean id="person" class="com.iweb.entity.Person"> <!-- 数组注入--> <property name="hobbiesArray"> <array> <value>games</value> <value>coding</value> </array> </property> <!-- list集合注入--> <property name="hobbiesList"> <list> <value>games</value> <value>coding</value> </list> </property> <!-- set集合注入--> <property name="hobbiesSet"> <set> <value>games</value> <value>coding</value> </set> </property> <!-- map集合注入--> <property name="hobbiesMap"> <map> <entry key="games" value="游戏" /> <entry key="coding" value="编码" /> </map> </property> </bean> <!-- Spring中管理了两种Bean 1. 普通Bean 在配置文件中定义的类型,与返回类型保持一致 2. FactoryBean 在配置文件中定义的类型和返回的类型可以不一致 表面上是 factoryBeanDemo 类, 实际上是该类下的 dog 类 原理类似于 @Configuration 和 @Bean 注解 --> <bean id="factoryBeanDemo" class="com.iweb.entity.FactoryBeanDemo" />
-
基于注解方式
Spring 对 bean 的管理提供了几种注解,注解的功能是一致的,都用来注册bean,但是注解本身所代表的含义不同
注解 含义 @Component 普通注解 @Service 业务层注解 @Controller 控制器注解(SpringMVC使用) @Repostiory DAO层注解(如果是 mybatis 一般会使用 @Mapper 或者在 springboot 中使用 @MapperScan) @Configuration 配置类注解 @Bean 配置类 bean 返回注解 @Qualifier 在使用了@AutoWired 注解的前提下,指定目标 name 所对应的 bean 进行注入 @AutoWired 根据类型自动装配(注入) @Resource 可以选择根据类型,或者 name 进行注入,在注入目标为接口类型的时候,如果接口只有一个实现类,可以直接通过类型注入,如果接口有多个实现类,可以配置 name 属性, 实现通过 name 指定注入 @Value 直接注入 @ComponentScan 注解扫描注解 @AutoWired 和 @Resource 区别:
- @AutoWired 来自于 spring @Resource 来自于 jdk 原生
- @AutoWired 是根据类型自动获取,如果想根据 name 获取,需要 @Qualifier 协助
- @Resource 是根据类型或 name 获取,如果提供 name 属性,则按照 name 获取,如果不提供则按照 type 获取
举个栗子:
// service层代码 // 等价于 <bean id="userService" class="com.iweb.service.Impl.UserServiceImpl" /> @Service(value = "userService") public class UserServiceImpl implements UserService { @Override public int add() { System.out.println("UserService => add()"); return 0; } } // mapper层代码,定义两个相同的子类,注册bean @Repository(value = "userMapper01") public class UserMapperImpl01 implements UserMapper { @Override public int add() { System.out.println("UserMapperImpl01 => add()"); return 0; } } @Repository(value = "userMapper02") public class UserMapperImpl02 implements UserMapper { @Override public int add() { System.out.println("UserMapperImpl02 => add()"); return 0; } }
AOP面向切面编程
AOP面向切面编程,但是AOP不等于 SpringAOP,SpringAOP 只是 AOP的一种实现方式而已,切面的内容和主业务逻辑是没有关系的,不管切面的数量有多少,都不会影响也不应该影响主业务的运行
注解方式配置AOP(底层:代理模式)
⭐要给AOP文件添加 @Component
和 @Aspect
注解
-
Before
前置增强在目标方法钱植入增强处理
@Before(value = "execution(* com.iweb.mapper.UserMapper.add(..))") public void before(JoinPoint joinPoint){ System.out.println("UserProxy => before()"); System.out.println("准备执行" + joinPoint.getTarget() + "的方法:" + joinPoint.getSignature().getName()); }
-
afterReturning
后置增强在目标方法正常执行(无异常)后植入增强处理
@AfterReturning(value = "execution(* com.iweb.mapper.UserMapper.add(..))", returning = "returnValue") public void afterReturning(JoinPoint joinPoint,Object returnValue){ System.out.println("UserProxy => afterReturning()."); System.out.println(joinPoint.getTarget() + "的方法" + joinPoint.getSignature().getName() + "的返回值为" + returnValue); }
-
afterThrowing
异常抛出增强在目标方法抛出异常的时候植入增强处理
@AfterThrowing(value = "execution(* com.iweb.mapper.UserMapper.add(..))",throwing = "exception") public void afterThrowing(JoinPoint joinPoint,Exception exception){ System.out.println("UserProxy => afterThrowing()"); System.out.println("调用" + joinPoint.getSignature().getName() + "方法发生异常" + exception.getMessage()); }
-
after
最终增强无论方法是否抛出异常,都会在目标方法之后植入增强处理,类似于异常处理机制中 finally 块的作用,一般用于释放资源
@After(value = "execution(* com.iweb.mapper.UserMapper.add(..))") public void after(JoinPoint joinPoint){ System.out.println("UserProxy => after()"); System.out.println("调用" + joinPoint.getSignature().getName() + "方法结束"); }
-
around
环绕增强在目标方法前后都可以植入增强处理,功能最强大的增强处理,可以获取或修改目标方法的参数,返回值,甚至可以控制目标方法是否执行
@Around(value = "execution(* com.iweb.mapper.UserMapper.add(..))") public Object around(ProceedingJoinPoint joinPoint){ System.out.println("UserProxy => around()"); try{ System.out.println("准备执行" + joinPoint.getTarget() + "方法" + joinPoint.getSignature().getName()); // 获取目标方法的返回值 Object result = joinPoint.proceed(); System.out.println("环绕增强处理之前的方法返回值为:" + result); return (Integer)result + 10086; }catch(Throwable e){ e.printStackTrace(); return null; } }