Spring是什么:
我们常说的Spring,其实在官网中全称是SpringFrameWork。
Spring是一个轻量级,非入侵式的Java开发框架(主要用于业务层,和整合其他层),解决了业务层和表现层、持久层的耦合问题,将面向接口编程贯穿整个框架,解决了企业级应用的复杂性,使JavaWeb的开发更加简单。
Spring开发方式:
Spring的核心是控制反转(IOC)和面向切面编程(AOP)
Spring通过控制反转和依赖注入(DI)可以很好的对程序进行解耦,通过配置文件(applicationContext.xml)或注解的方式对类进行管理。通过Spring面向切面的编程方式,可以对指定代码进行增强。
使用Maven工程构建,pom文件需要的基础坐标依赖:
<dependencies>
<!--maven传递依赖,导入依赖jar包:包括spring核心依赖包:spring-core、spring-beans、spring-aop等-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--Spring框架整合JUnit单元测试,必须先有Junit单元测试的环境-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- AOP联盟 -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- Spring Aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- aspectj -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.3</version>
</dependency>
<!--Spring事务管理相关-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
什么是IoC(Inversion of Control,控制反转):
IoC是一种设计思想,将对象的创建权利(new)反转(交给)Spring框架,解决程序耦合度过高的问题。即原来是程序主动创建对象,控制权在我们手上;现在由Spring创建,管理,注入对象,控制权在Spring手上。
回想一下,之前我们在service层,经常会出现这样的代码:
public class OrderServiceImpl implements OrderService {
//持久层对象,直接在程序中创建对象
private OrderDao orderDao = new OrderDaoImpl();
//其他逻辑
}
如果我们的业务在后续发生变化,业务层调用持久层的实现类发生变动,那么有关new OrderDaoImpl()的程序,要全部发生变化,虽然在这个例子中改变并不明显,但在真正的企业级应用开发中,牵一发而动全身,由于项目大且复杂,修改相关代码是一件吃力且昂贵的事情。
那么现在我们修改一下代码:
public class OrderServiceImpl implements OrderService {
//持久层对象,直接在程序中创建对象
private OrderDao orderDao;
//利用set方法,动态实现对象的赋值。
public void setOrderDao(OrderDao orderDao){
this.orderDao = orderDao;
}
//其他逻辑
}
其实仅仅是一个set方法,就发生了革命性的变化。使用set方法后,对象不在由程序主动创建,而是成为了被动接受的对象。主动权给了调用者,即我们只需要改动传入对象的代码即可。
这种思想,使开发人员不用再主动去管理对象的创建,使系统耦合性大大降低,这种逻辑就是IOC的底层原理。
什么是DI(Dependency Injection,依赖注入):
Sprig的IoC需要DI,在Spring负责创建Bean对象时,动态的将其依赖的对象注入到Bean对象中。
Spring最核心的依赖注入,本质就是用set方法注入。使用bean标签下的property。
我们原来的业务逻辑:
SpringIoC的业务逻辑:
Spring配置文件开发
Spring框架在初始化时先读取配置文件,来决定管理哪些类,把哪些对象注入到另外的对象中去。
Spring配置文件命名格式:applicationContext.xml,使用schema约束。
Spring配置文件通过bean标签管理对象(自定义的类,Spring框架的类),交给IOC容器。
使用配置文件后,我们就只需要在配置文件中修改相关bean,即可解决上面所阐述耦合度问题。
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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--把UserServiceImpl类交给IoC容器进行管理,对象由IoC容器来创建,bean标签管理
id:对象在IoC容器唯一的名称
class:管理类的全路径(底层通过反射创建对象)
scope:创建对象的生命周期(作用域)
singleton(单例,加载配置文件,就会创建实例,且IOC容器只存在该类一个实例。生命周期和容器生命周期一致) 开发中大部分都是这个模式
prototype(多例,每次从IOC容器中获取对象时,才会创建一个实例。容器不负责销毁对象,由GC销毁)
request(多例,每次请求都会创建一个实例,存到request域对象,生命周期为一次请求)
session(多例,每次会话都会创建一个实例,存到session域对象,生命周期为一次会话)
init-method:当加载bean,创建对象后,调用init-method属性指定的方法
destroy-method,当bean从容器中删除时,调用destroy-method属性指定的方法
-->
<!--实例化bean的三种方式,第一种最常用-->
<!--1.默认调用无参构造构造器实例化对象-->
<bean id="usImpl" class="cn.cx.service.UserServiceImpl" scope="singleton" init-method="init" destroy-method="destroy"/>
<!--2.静态工厂方式实例化对象,class写我们自定义的静态工厂类,factory-method写该类里的静态方法,返回一个我们需要的实例对象
<bean id="usImpl" class="xx.xx.xx.StaticFactory" factory-method="method"/>
-->
<!--3.动态工厂方式实例化对象,先配置一个bean,定义id和类的全路径,然后再配置一个bean,factory-bean:自定义的动态工厂,factory-method该类中的方法,返回一个我们需要的实例对象
<bean id="dfactory" class="xx.xx.xx.Dfactory"/>
<bean id="usImpl" factory-bean="dfactory" factory-method="method"/>
-->
<!--DI:属性的依赖注入-->
<!--
之前的方式:UserServiceImpl usImpl = new UserServiceImpl();
现在创建对象,并给对象赋值由Spring完成。
id相当于变量名,class相当于对象类型,property相当于对象,其中的name相当于属性名,value相当于属性值
底层用set方法实现
要想实现Bean的自动装配,要在bean标签加上autowire属性,常用的值类型有byName和byType
-->
<bean id="osImpl" class="cn.cx.service.OrderServiceImpl">
<!--将odImpl注入到osImpl的orderDao属性上,引用数据类型用ref-->
<property name="orderDao" ref="odImpl"/>
<!--普通数据类型用value-->
<property name="username" value="lisichen"/>
<property name="size" value="45"/>
</bean>
<!--DI:构造器的依赖注入-->
<bean id="osImpl1" class="cn.cx.service.OrderServiceImpl1">
<constructor-arg name="orderDao" ref="odImpl"/>
<constructor-arg name="username" value="duansl"/>
<constructor-arg name="size" value="78"/>
</bean>
<bean id="odImpl" class="cn.cx.dao.OrderDaoImpl"></bean>
<!--DI:数组,集合,Properties的依赖注入-->
<bean id="cb" class="cn.cx.demo.CollectionBean">
<property name="strs">
<array>
<value>断舍离</value>
<value>立思辰</value>
<value>王思傲</value>
</array>
</property>
<property name="list">
<list>
<value>计算机</value>
<value>互联网</value>
<value>电商</value>
</list>
</property>
<property name="map">
<map>
<!--如果是引用类型,加-ref-->
<entry key="1" value="a"/>
<entry key="2" value="b"/>
<entry key="3" value="c"/>
</map>
</property>
<property name="prop">
<props>
<prop key="username">root</prop>
<prop key="password">adssd</prop>
</props>
</property>
</bean>
<!--可以引入其他加载文件,多人开发时使用
<import resource="xxxx.xml"/>
-->
</beans>
调用对象的逻辑:
public class Test {
/**
* 我们第一开始管理对象的方式:由我创建
* */
@org.junit.Test
public void test(){
UserService us = new UserServiceImpl();
us.hello();
}
/**
* 使用IoC方式管理对象:交给SpringIoC创建
* */
@org.junit.Test
public void testIoc(){
//创建SpringIoc的工厂(Spring上下文对象),加载applicationContext.xml配置文件,将配置文件中指定的类创建成对象,存入IOC容器中
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
/*可以以可变参数形式,加载多个配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml","xxx.xml","xxxx.xml");*/
System.out.println("=================");
//从容器中获取对象(底层是一个Map集合),传入参数是配置文件中对应类的id值
UserService usImpl = (UserService) ac.getBean("usImpl");
usImpl.hello();
}
}
Spring注解开发:
想使用注解,需要在Spring配置文件中引入相关schem约束的名称空间,并开启注解扫描:
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解扫描 cn.cx.所有的包中的所有的类 -->
<context:component-scan base-package="cn.cx"/>
</beans>
之后想要将哪个类交给IOC容器管理,就在哪个类上加上对应注解。
IOC管理常用注解:
@Component:把注解的类加入IOC容器管理,在非三层类上使用。
@Repository:作用同@Component,在持久层使用。
@Service:作用同@Component,在业务层使用。
@Controller:作用同@Component,在控制层使用。
DI常用注解(实现Bean的自动装配):
@Value:用于注入普通类型(String,int,Double等)
@Autowired:用于注入引用类型,默认按类型进行自动装配(byType),使用后可以省略对应的set方法,如果进行注解的类没有加上IoC管理的注解,编辑器会报错
@Qualifier:强制按id名称注入,不能单独使用,要与@Autowired一起使用
@Resource:Java的原生注解,也可以实现自动装配,默认通过byName查找,如果匹配不到,再通过byType查找
替换扫描包的注解:
@Configuration:声明当前类为配置类
@ComponentScan:扫描指定包结构,与@Configuration一起使用,可代替 <context:component-scan base-package=“cn.cx”/>
@Import:引入新的配置类
@Bean:把该方法创建的对象存入到IoC容器中,当有一些开源对象需要使用注解时,如果采用纯注解开发,我们无法在其源码上加注解,就可以使用该标签。
Spring整合Junit单元测试的注解:
@RunWith
@ContextConfiguration,与@RunWith一起使用,可以代替加载配置文件的API使用。
什么是AOP(Aspect Oriented Programming):
意为面向切面编程,是一种编程规范,采取横向抽取机制,采取横向抽取机制,优化了传统纵向继承体系的重复代码,可以在不修改程序的前提下,进行增强,解耦。
用于处理系统中分布于各个模块的横切关注点,比如事务管理、权限控制、缓存控制、日志打印等等。
SpringAOP底层原理:
JDK动态代理:代理对象实现真实对象功能(还可增强功能)。生成代理对象必须实现被代理对象所实现的相同接口(基于接口),由java反射包下的Proxy类和InvocationHandler接口实现动态代理功能,Proxy提供了创建动态代理类和实例的静态方法,InvocationHandler处理代理实例上的方法调用并返回结果(通过invoke方法),其本质是通过反射实现的。
CGLIB代理:生成代理对象继承被代理对象(基于类)
AOP术语:
Joinpoint(连接点):被代理类中(实现类)的各个方法
Pointcut(切入点):程序增强的入口,需要编写表达式(Spring框架使用AspectJ切入点表达式语言),指示程序哪些方法进行增强(自己编写),可以将切入点理解为需要被拦截的连接点
Advice(通知/增强):切面类中的方法,代码的具体逻辑,有前置、后置、最终、异常、环绕几种(自己编写)
Target(目标对象): 被代理对象
Proxy(代理):代理对象(真正执行方法的)
Aspect(切面):切入点+增强构成切面,自定义的切面类(增强方法写在里面)要先加入IOC容器。
Weaving(织入):把增强逻辑应用到被代理对象来创建新的代理对象的过程。
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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--配置切面类,把该类交给IOC容器管理-->
<bean id="myXmlAspect" class="cn.cx.aop.MyXmlAspect"/>
<!--配置AOP的通知增强-->
<aop:config>
<!--使用切面类-->
<aop:aspect ref="myXmlAspect">
<!--
method()表示切面类中的方法(执行的增强方法)
pointcut表示切入点:save()
-->
<aop:before method="log" pointcut="execution(public void cn.cx.aop.OrderServiceImpl.sava())"/>
</aop:aspect>
</aop:config>
</beans>
AOP通知类型:
前置通知aop:before:目标方法执行前进行增强
最终通知aop:after:目标方法执行成功或者失败进行增强
后置通知aop:after-returning:目标方法执行成功进行增强
异常通知aop:after-throwing:目标方法执行失败进行增强
环绕通知aop:around:目标方法执行前后都可以进行增强。目标对象方法需要手动执行
切入点表达式语法:
execution():固定写法。
public可以省略不写,返回值必须写,返回值可以用 * 通用的表示,重载方法都会被查找到。
包名+类名必须写,也可以用 * 通用表示,如cn.tx. * /cn.tx. * . * /cn.tx. * . * ServiceImpl。
方法名必须写,也可以使用*表示全部方法。
参数列表,(…)表示任意类型和个数的参数,可以想成可变参数。
AOP注解开发,要在配置文件中先加上:
<!--开启自动代理-->
<aop:aspectj-autoproxy/>
AOP常用注解:
@Aspect:声明当前类切面类,相当于<aop:aspect ref=“xxx”>
@Before:在切面类中增强方法上使用该注解,相当于<aop:before >,后置,最终效果和该标签同理。
@After: 后置通知:目标方法之后执行(始终执行)
@AfterReturning:返回之后通知:执行方法结束之前执行(异常不执行)
@Around:环绕通知:环绕目标方法执行
@AfterThrowing:异常通知:出异常后执行
@EnableAspectJAutoProxy:开启AOP自动代理
Spring加载属性文件:
<!--加载properties属性文件-->
<context:property-placeholder location="classpath:xxx.properties"/>
Spring声明式事务管理:
相关的类:
PlatformTransactionManager:平台事务管理器,不同的持久层框架,使用不同的实现类。
TransactionDefinition:事务定义信息类,传播行为(一个方法内调用另一方法,牵扯到两个事务)、超时、只读等等。
TransactionStatus:事务状态。
事务管理配置文件格式:
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--配置事务的通知(没有自己编写切面类,通知方法也不是自己编写,Spring框架提供的)-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--对方法进行增强,设置隔离级别,传播行为,超时的时间-->
<tx:method name="xxx" isolation="DEFAULT" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<!--配置AOP的增强-->
<aop:config>
<!--Spring框架提供系统通知,使用advisor标签-->
<aop:advisor advice-ref="txAdvice" pointcut="execution(public * cn.cx.pay.AccountServiceImpl.pay(..))" />
</aop:config>
</beans>