Java进阶笔记(三):Spring相关
【总体部分】
Spring框架的目的:让开发者专注于业务,而不是基本实现。
------------------
Spring的优势:
方便解耦,简化开发
支持AOP编程
支持声明式事务(@Transactional)
方便程序测试(Junit)
方便集成其它优秀框架
降低JavaEE API的使用难度(spring封装了一些jdk中的方法)
------------------
Spring可分为4个模块(开发时只需要引入需要的模块即可):
Web(MVC、WebSocket、Servlet等)、Data Access(jdbc等数据操作相关)、Core Container(Core、Context等核心部分)、Test
==================
【IoC部分】
IoC(控制反转)的作用:解决对象之间的耦合问题。
例子:直接new实现类(XXXServiceImpl),当需要变更实现类(例如名称发生变化,要使用XXXServiceImpl2),所有new的地方也需要修改,这是强耦合;
而使用IoC后,不需要new实现类,只需要将接口类写入xml中,然后从工厂中获取即可,达到了解耦的目的。
<beans>
<bean id="xxxService" class="com.test.service.impl.XXXServiceImpl"></bean>
</beans>
只需要修改配置文件中的"XXXServiceImpl"为"XXXServiceImpl2"即可,只有一个地方。(其它地方是根据id从工厂中拿出来的,无需修改)
------------------
IoC和DI的区别:
DI:Dependency Injection(依赖注入)
IOC和DI描述的是同一件事情,但是角度不一样;IoC是对象的角度(不用自己创建对象了),DI是容器的角度(容器把对象依赖的其它对象注入)
------------------
spring中IoC的配置方式:
1.使用xml
2.使用xml+注解
3.使用纯注解
目的就是将类对象交给spring管理,将对象依赖关系交给spring实现。(@Component/@Controller/@Service/@Repository,@Autowired/@Resource)
使用对象时,从spring工厂获取。
------------------
Spring中Bean的生存周期(xml中可以用scope定义bean的作用范围):
singleton:单例,IOC容器中只有一个该类对象【默认为这个】
prototype:原型(多例),每次使用该类的对象(getBean),都返回一个新的对象【spring只负责创建,不负责管理这种类型的对象】
request:web应用中使用,指明bean的生命周期为request;基本不用
session:web应用中使用,指明bean的生命周期为session;基本不用
application:web应用中使用,指明bean的生命周期为application;基本不用
websocket:web应用中使用,指明bean的生命周期为websocket;基本不用
------------------
使用xml注入bean,配置<bean>标签时:
init-method:可以指定bean被初始化时调用的方法
destroy-method:可以指定bean被销毁时调用的方法(只有当对象为singleton时才有效)
------------------
使用xml注入bean,给bean中的变量设置值时,set注入方式需要使用property标签,注入普通属性使用value,注入另外一个bean使用ref。
<bean id="..." class="...">
<property name="username" value="abc"/>
<property name="JavaBean" ref="javaBean"/>
</bean>
构造器注入方式,需要使用constructor-arg标签,index可根据参数顺序传值,也可以使用name(同上)。
<bean id="..." class="...">
<constructor-arg index="0" value="abc"/>
<constructor-arg index="1" ref="javaBean"/>
</bean>
------------------
第三方jar包的bean可以定义在xml中,交给spring管理,例如Druid连接池。
------------------
xml中用bean标签配置的对象,等价于用@Component写在相应的类文件中。
@Component注解有三种别名,可以用来区分,分别是@Controller(控制层),@Service(服务层),@Repository(Dao层)
------------------
@Autowired与@Resource:
将类对象注入spring容器后,使用注解@Autowired,@Resource注入其它有依赖关系的对象。
@Resource默认按照名称注入(id),【jdk11中移除了,想使用的话需要引入jar包,javax.annotation-api】
@Autowired按照类型注入,如果按照类型无法锁定唯一对象,可以结合@Qualifier指定具体的id
例子:
@Autowired
@Qualifier("xxxDao")
private XXXDao xxxDao;
------------------
spring扫描包语句样例:
在applicationContext.xml中写:
<!-- 开启注解扫描,base-package指定扫描的包路径 -->
<context:component-scan base-package="com.xxx.test" />
<!-- 引入外部资源文件 -->
<context:property-placeholder location="classpath:jdbc.properties" />
<!-- 第三方jar,druid连接池;配置了properties后就可以使用$占位符了 -->
<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>
------------------
Spring包扫描语句样例2:
如果是WEB应用,则在web.xml中使用以下语句:
<!-- 注解模式需要的参数 -->
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<!-- 配置Spring配置类的类名 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.xxx.test.SpringConfig</param-value>
</context-param>
<!-- 使用监听器启动spring的IOC容器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
------------------
Spring中Bean对象的延迟加载(懒加载):配置某个对象的lazy-init,默认是false,创建容器后即初始化对象;设置为true后,当get对象时才初始化该对象。
------------------
IoC容器包括map集合(单例池),beanfactory,BeanPostProcessor等,不只是map集合。
------------------
spring循环依赖问题:(A->B->A)
1.单例bean构造器参数循环依赖(无法解决)
2.prototype原型bean循环依赖(无法解决)
3.单例bean通过setXXX或者@Autowired进行循环依赖,可以解决。
解决方法:三级缓存机制。
(1)创建对象A时,实例化之后就立即放入三级缓存。
(2)创建A途中发现依赖于对象B(A用到了B),查找一二三级缓存没有发现B,则开始创建B对象。
(3)创建B对象途中(实例化后也立即放入三级缓存),发现依赖于对象A,从缓存中查找,在三级缓存中找到了。
(4)从三级缓存找到A后,将A移入二级缓存,同时对A进行一些扩展操作,之后完成对象B的创建。
(5)对象B创建完毕,把自己移入一级缓存。
(6)继续创建对象A,直接拿到创建好的对象B(发现需要B时,缓存中没找到B,就执行创建B的方法,现在执行完毕了,所以直接拿到),之后完成A对象的创建。
========================
【AOP部分】
spring中AOP的配置方式:
1.使用xml
2.使用xml+注解
3.使用纯注解
spring使用动态代理实现AOP:
spring实现AOP时,如果被代理对象有接口,会使用jdk动态代理;如果被代理对象没有接口,则使用cglib代理。
使用xml配置AOP步骤:
(1)导入2个jar包,spring-aop和aspectjweaver
(2)创建一个XXXUtils.java,其中写好一些方法,当做切入点方法
(3)在xml中使用bean标签引入XXXUtils.java
(4)在xml中使用aop:config标签,配置切入点方法、方位信息(配置需要在哪个方法之前或之后执行切入的方法)
(5)方位信息可以配置的类型有:前置、后置、异常、最终(无论是否异常都执行)、环绕(其中有个方法可以控制原有业务逻辑是否执行,类似invoke,因此比较灵活)
(6)因此环绕通知不要与普通通知方式混用
(7)aop:pointcut标签中,注意aspectj表达式的用法
---------------------------
xml与注解配置AOP:
1.xml中使用<aop:aspectj-autoproxy/>开启aop注解驱动
2.XXXUtils.java中使用注解配置切面方法
---------------------------
纯注解配置AOP:
在配置类上使用@EnableAspectJAutoProxy注解开启AOP(代替<aop:aspectj-autoproxy/>)
=============================
【事务部分】
Spring如何实现事务控制:
使用动态代理与AOP思想实现控制,增加3个步骤:
1.关闭jdbc connection自动提交(一个事务要使用一个connection);
//然后执行被代理的事务步骤;
2.发现执行异常则执行rollback();
3.正常执行完毕则执行commit()。
-----------------------------
编程式事务:在业务代码中添加事务控制代码
声明式事务:通过xml或者注解配置的方式达到事务控制的目的
-----------------------------
注解形式配置spring事务:
1.spring配置类上加注解:@EnableTransactionManagement
2.使用@Transactional,可以加在接口上、实现类上、方法上
=============================
【总结】
Spring到底做了什么:
1.使用反射,创建与管理类对象,配置好类对象之间的依赖关系。(IoC)
2.使用动态代理,增强代码功能,实现事务。(AOP)