Spring框架概述
1. 轻量级框架
2. 降低开发复杂性
3. IOC和Aop
4. 优点多多
入门案例
1. 下载spring
2. 导入相关jar包
3. 创建普通类,编写该类方法
4. xml配置
5. 代码编写:加载配置文件,获取配置创建的对象
IOC容器:Inversion Of Control 控制反转
1. IOC底层原理
- IOC底层原理:xml解析 + 工厂模式 + 反射(单纯使用工厂模式耦合度不够低)
- IOC过程:
第一步:xml配置
第二步:创建工厂类:Class.forName(class属性值).newInstance()
2. IOC接口
- IOC容器实现的两种方式:
- BeanFactory接口,一般是spring内部使用—>获取对象时才创建对象(懒汉式)
- ApplicationContext接口:BeanFactory的子接口,更强大—>加载配置文件时就创建对象(饿汉式)----更好,在启动服务器时就已经创建好对象
-
ApplicationContext接口的两个主要实现类:
FileSystemXmlApplicationContext("带盘符的路径") ClassPathXmlApplicationContext("src下的路径")
3. IOC操作Bean管理:即Spring创建对象、注入属性
基于xml方式管理bean
创建对象:
- xml配置文件中bean标签配置
- 中属性:id,class,name(用于struct)
- 默认使用无参构造
注入属性:DI(依赖注入)
(1)使用set方法注入:定义类属性及其set方法,配置xml文件<bean><property name="属性名" value="值"></property></bean>
(2)使用有参构造器注入:定义类有参构造方法,配置xml文件<bean><constructor-arg name="方法参数名" value="值"></constructor-arg></bean>
(3)特别地:p名称空间注入:添加p名称空间xmlns:p=".../chema/p"
,就可以直接在bean标签中添加p:属性=""
(4)注入属性特殊值:
1.null值:不填value,标签中加<null/>
2.属性值含特殊值:<![CDATA[属性值]]>
3.注入外部bean:<property name="属性名" ref="外部bean的id">
4.注入内部bean:<property>中省略value,里面嵌套一个bean---->外部bean也可以实现
5.注入级联赋值:(1)类似注入外部bean的方式;(2)在1的基础上加<property name="级联的bean.属性" value="值">(需要内部bean的get方法)
6.注入数组:<property>中加入<array><value>值</value></array>
7.注入List:<property>中加入<list><value>值</value></list>
8.注入set:<property>中加入<set><value>值</value></set>
9.注入Map:<property>中加入<map><entry key="" value=""></map>
10.集合里的值为对象类型:<property>中加入<list><ref bean="另一个bean的id"></ref></list>
11.把集合注入提取为公共部分:添加util名称空间-xmlns:util="...",xsi:schemaLocation="...",<util:list id="公共标识"><value></value></util:list>
两种bean
- 普通bean:配置文件中class属性就决定了返回的类型
- FactoryBean:配置文件中class属性和返回的类型可以不一样,创建一个类实现FactoryBean接口,重写方法getObject()定义要返回的类型
bean的作用域:
bean标签中scope属性设置创建的bean实例是单例(默认)还是多例
值为singleton:创建单例对象,在加载配置文件时就创建
值为prototype:创建多例对象,在getBean()时才创建对象
值为request:会把创建的对象放到request域中(了解)
值为session:会把创建的对象放到session域中(了解)
bean的生命周期:
(1)通过无参构造器创建bean实例
(2)调用set方法为bean属性赋值或引用其他bean
(~)调用把bean实例传递给bean后置处理器(BeanPostProcessor)的方法--postProcessBeforeInitialization
(3)调用bean的初始化方法(需手动配置)---<bean init-method="bean类中的方法名">
(~)调用把bean实例传递给bean后置处理器(BeanPostProcessor)的方法--postProcessAfterInitialization
(4)使用bean
(5)调用bean的销毁方法(需手动配置)---<bean destroy-method="bean类中的方法名">,ClassPathXmlApplicationContext.close()
xml自动装配:
很少用到,一般用注解
<bean>标签属性autowire="byName"----->根据属性名称自动装配
<bean>标签属性autowire="byType"----->根据类型自动装配
引入外部属性文件(properties):
(1)引入context名称空间
(2)<context:property-placeholder location="classpath:properties路径" />
(3)<property>的value="${key}"
基于注解方式管理bean:
bean管理创建对象的注解:
功能都是创建对象
@Component
@Service-----service层
@Controller-----web层
@Repository------dao层
创建对象:
1. 引入aop依赖
2. 开启组件扫描:引入context名称空间,<context:component-scan base-package="扫描包路径">
3. 在类前加@注解(value="实例对象名")----括号内容不写默认实例对象名为小驼峰类名
组件扫描细节配置
<!--示例 1
use-default-filters="false" 表示现在不使用默认 filter,自己配置 filter
context:include-filter ,设置扫描哪些内容
-->
<context:component-scan base-package="com.atguigu" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--示例 2
下面配置扫描包所有内容
context:exclude-filter: 设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.atguigu">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
注入属性:
-
@Autowired:根据属性类型注入
-
@Qualifier:根据属性名称注入,配合@Autowired使用
第一步:创建service类和dao类并在类前添加创建对象注解 第二步:在service类中的dao属性前加@Autowired注解,若dao实现类有多个,可以加@Qualifier(value="dao实现类名")
-
@Resource:根据名称类型都可,不属于spring,来自java扩展包
@Resource:根据类型注入 @Resource(name="dao实现类名"):根据属性名称注入
-
@Value:普通类型注入
在属性前@Value(value="属性值")
纯注解开发:
(1)@Configuration+@ComponentScan(basePackages={"扫描包路径"}) 注解一个配置类,代替xml配置
(2)new AnnotationConfigApplicationContext(配置类)
AOP
什么是AOP
面向切面编程,降低业务逻辑各部分耦合度
通俗描述:不通过修改源代码的方式,在主干功能里添加新功能
AOP底层原理
底层使用的是动态代理来增强方法:
1. 有接口情况,使用JDK动态代理
2. 无接口情况,使用CGLIB动态代理
JDK动态代理底层实现
public class ProxyTest {
public static void main(String[] args) {
Class[] interfaces = {UserDao.class};
UserDao userDao = new UserDaoImpl();
UserDao proxyInstance =(UserDao)Proxy.newProxyInstance(ProxyTest.class.getClassLoader(), interfaces, new ProxyUserDao(userDao));
System.out.println(proxyInstance.add(12,3));
}
}
class ProxyUserDao implements InvocationHandler{
private Object obj;
public ProxyUserDao(Object obj){
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("原方法之前执行"+method.getName()+">>>>"+ Arrays.toString(args));
Object res = method.invoke(obj, args);
System.out.println("原方法之后执行"+obj);
return res;
}
}
AOP操作术语
-
连接点:类中可以被增强的方法
-
切入点:实际要增强的方法
-
通知(增强):实际增加的逻辑代码
通知类型:
- 前置通知:@Before(value=“切入点表达式”)
- 环绕通知:@Around(value=“切入点表达式”)
- 异常通知:@AfterThrowing(value=“切入点表达式”)
- 最终通知:@After(value=“切入点表达式”)—方法后执行,出异常也执行
- 后置通知:@AfterReturning(value=“切入点表达式”)—返回值之后执行,出异常不执行
-
切面:把通知应用到切入点的过程
切入点表达式
作用:知道增强哪个类哪个方法
execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表]))
execution(* com.spring.dao.add(..));//某个类某个方法
execution(* com.spring.dao.*(..));//某个类所有方法
execution(* com.spring.*.*(..));//包下所有类所有方法
基于AspectJ实现AOP操作
AspectJ是一个独立的AOP框架,使用需引入相关依赖
1. 基于xml配置文件(了解)
-
创建增强类,编写不同方法代表不同通知类型
-
xml中配置
-
xml中配置aop
<aop:config> <aop:pointcut id="切入点名" expression="切入点表达式"/> <aop:aspect ref="增强类名"> <aop:before method="增强的方法名" pointcut-ref="切入点名" /> </aop:aspect> </aop:config>
2. 基于注解(常用)
使用步骤
-
创建增强类,编写不同方法代表不同通知类型
-
在Spring配置文件中,使用context、aop名称空间开启注解扫描
<context:component-scan base-package="扫描包路径"></context:component-scan>
-
在被增强类和增强类前加创建对象注解
-
在增强类前加@Aspect生成代理对象
-
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
编写不同类型的通知,在通知方法前加通知类型注解
//特别的: @Around(value="execution(....)") public void around(ProceedingJoinPoint proceedingJoinPoint){ //环绕之前的代码 proceedingJoinPoint.proceed(); //环绕之后的代码 }
抽取公共切入点:
```java
@Pointcut(value="共用的切入点表达式")//写在增强类的一个方法前,其他方法使用@注解(value="方法名()")
```
设置增强类优先级:
```
@Order(数字)//加在增强类前,值越小优先级越高
```
全注解开发:
-
编写config类,类前加注解:
@Configuration @ComponentScan(basePackages={"扫描包路径"}) //开启注解扫描 @EnableAspectJAutoProxy(proxyTargetClass=true)
JdbcTemplate
JdbcTemplate是什么
Spring框架对JDBC的封装
JdbcTemplate使用步骤
-
导包
-
配置文件中配置数据库连接池
-
配置JdbcTemplate对象,并注入dataSource属性
<context:property-placeholder location="jdbc.properties"></context:property-placeholder> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="url" value="${url}"></property> <property name="username" value="${user}"></property> <property name="password" value="${password}"></property> <property name="driverClassName" value="${driver}"></property> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean>
-
以service类和dao类为例,在类前添加创建对象注解,在service类中注入dao,在dao中注入JdbcTemplate
//在dao中注入jdbcTemplate @Autowired private JdbcTemplate jdbcTemplate;
JdbcTemplate操作数据库
-
添加、修改、删除
jdbcTemplate.update(String sql,Object ... args)
-
查询返回某个值
jdbcTemplate.queryForObject(String sql,Class<T> requiredType) //这是的Class<T>是返回类型的Class,如Integer.class
-
查询返回某个对象
jdbcTemplate.queryForObject(String sql,RowMapper<T> rowMapper,Object ...args) //RowMapper<T>是一个接口,需要的是其实现类对象,完成数据封装: new BeanPropertyRowMapper<T>(T.class)
-
查询返回集合
jdbcTemplate.query(String sql,RowMapper<T> rowMapper,Object ...args) //RowMapper<T>是一个接口,需要的是其实现类对象,完成数据封装: new BeanPropertyRowMapper<T>(T.class)
-
批量添加、修改、删除
batchUpdate(String sql,List<Object[]> batchArgs) //Object[]即添加的单个数据,返回int[]
Spring中的事务
事务概念回顾
四个特性:ACID
事务一般添加到三层结构中的service层(业务逻辑层)
原始代码处理事务
try{
//1.开启事务
//2.业务操作
//3.提交事务
}catch(Exception e){
//4.回滚
}
Spring中处理事务
Spring事务管理API
PlatformTransactionManager接口
DataSourceTransactionManager实现类
编程式事务管理(了解)
声明式事务管理(常用)
Spring进行声明式事务管理时,底层使用的是AOP原理
基于xml配置文件方式(了解)
-
配置文件中添加事务管理器,并用set方式注入数据源
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
-
配置通知,配置事务参数,指定哪个方法添加事务
//配置通知 <tx:advice id="txadvice"> //配置事务参数 <tx:attributes> //指定哪个方法添加事务 <tx:method name="方法名" propagation="..."/> //可以用*匹配 </tx:attributes> </tx:advice>
-
配置切入点和切面
<aop:config> <aop:pointcut id="pt" expression="切入点表达式" /> <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/> </aop:config>
基于注解方式(常用)
-
配置文件中添加事务管理器,并用set方式注入数据源
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
-
配置文件中添加tx名称空间,开启事务注解
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
-
service类或该类方法前加事务注解@Transactional
@Transactional中可以添加参数:@Transactional(propagation=Propagation.REQUIRED,…)
-
propagation:事务传播行为:多个事务方法互相调用过程
事务传播行为有七种:
REQUIRED(默认):有事务则直接运行,没事务则自己启动事务
REQUIRED_NEW:直接启动自己的事务,挂起其他事务
-
isolation:设置事务隔离级别,解决脏读、不可重复读、幻读
-
timeout:超时时间:事务在一定时间内提交,不然就回滚,默认值-1,自定义以秒为单位
-
readOnly:是否只读,默认为false
-
rollbackFor:设置哪些异常进行回滚
-
noRollbackFor:设置哪些异常不进行回滚
完全注解开发:
@Configuration //配置类 @ComponentScan(basePackages = "com...") //组件扫描 @EnableTransactionManagement //开启事务 public class config { //创建数据库连接池 @Bean public DruidDataSource getDruidDataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql:///user_db"); dataSource.setUsername("root"); dataSource.setPassword("root"); return dataSource; } //创建 JdbcTemplate 对象 @Bean public JdbcTemplate getJdbcTemplate(DataSource dataSource) { //到 ioc 容器中根据类型找到 dataSource JdbcTemplate jdbcTemplate = new JdbcTemplate(); //注入 dataSource jdbcTemplate.setDataSource(dataSource); return jdbcTemplate; } //创建事务管理器 @Bean public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(dataSource); return transactionManager; } }
-
Spring5新特性
Spring5的代码基于Java8,兼容Java9
Spring5自带通用的日志封装
-
Spring5移除了Log4jConfigListener,使用Log4j2
-
Spring5整合Log4j2步骤:
-
导包
-
创建log4j2.xml
<?xml version="1.0" encoding="UTF-8"?> <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> <!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出--> <configuration status="INFO"> <!--先定义所有的appender--> <appenders> <!--输出日志信息到控制台--> <console name="Console" target="SYSTEM_OUT"> <!--控制日志输出的格式--> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </console> </appenders> <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效--> <!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出--> <loggers> <root level="info"> <appender-ref ref="Console"/> </root> </loggers> </configuration>
也可以手动输出日志:
Logger log = LoggerFactory.getLogger(所在类.class); log.info("输出信息"); log.warn("输出信息");
-
@Nullable注解
Spring5核心容器支持@Nullable注解,该注解可以用在方法、属性、参数上,表示可为空
函数式风格
Spring5核心容器支持函数式风格GenericApplicationContext
//函数式风格创建对象,交给 spring 进行管理
@Test
public void testGenericApplicationContext() {
//1 创建 GenericApplicationContext 对象
GenericApplicationContext context = new GenericApplicationContext();
//2 调用 context 的方法对象注册
context.refresh();
context.registerBean("user1", User.class, () -> new User());
//3 获取在 spring 注册的对象
// User user = (User)context.getBean("com.atguigu.spring5.test.User");
User user = (User) context.getBean("user1");
System.out.println(user);
}
整合单元测试
整合JUnit4
-
导包
-
创建测试类
@RunWith(SpringJUnit4ClassRunner.class) //单元测试框架 @ContextConfiguration("classpath:bean1.xml") //加载配置文件 public class JTest4 { @Autowired private AccountService accountService; @Test public void test1() { accountService.accountMoney(); } }
整合JUnit5
-
导包
-
创建测试类
@ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:bean1.xml") //使用一个复合注解替代上面两个注解完成整合 //@SpringJUnitConfig(locations="classpath:bean1.xml") public class JTest5 { @Autowired private AccountService accountService; @Test public void test1() { accountService.accountMoney(); } }
WebFlux(补充)
前置知识:Spring MVC、Spring Boot、Maven、Java 8新特性
WebFlux是什么
-
是 Spring5 添加新的模块,用于 web 开发的,功能和 SpringMVC 类似的,Webflux 是使用响应式编程的框架
-
使用传统 web 框架,比如 SpringMVC,这些基于 Servlet 容器,Webflux 是一种异步非阻塞的框架,异步非阻塞的框架在 Servlet3.1 以后才支持,核心是基于 Reactor 的相关 API 实现的
-
什么是异步非阻塞
- 异步和同步针对调用者,调用者发送请求,如果等着对方回应之后才去做其他事情就是同步,如果发送请求之后不等着对方回应就去做其他事情就是异步
- 阻塞和非阻塞针对被调用者,被调用者受到请求之后,做完请求任务之后才给出反馈就是阻塞,受到请求之后马上给出反馈然后再去做事情就是非阻塞