一、Spring概述
Spring是一个开源的JavaEE的全功能栈的应用程序框架,Spring致力于提供一个以统一的、高效的方式构造整个应用,并且可以将单层框架以最佳的组合揉和在一起建立一个连贯的体系。可以说Spring是一个提供了更完善开发环境的一个框架,可以为POJO(Plain Ordinary Java Object)对象提供企业级的服务。
特点:
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。
控制反转——Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
容器——Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。
框架——Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。
所有Spring的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。它们也为Spring中的各种模块提供了基础支持。
优势:
方便解耦,简化开发
AOP编程的支持
声明式事务的支持
方便程序的测试
方便整合其他框架(ssm)
简化JavaEE的API的使用
二、耦合以及解耦
耦合是指程序间的依赖关系,包括了类之间的依赖和方法的依赖
降低程序间的依赖
开发中,要做到:编译期不依赖,运行期依赖
思路分析:
应该使用反射创建对象,尽量避免使用new关键字
使用配置文件,存入全路径的信息
三、Spring核心----IOC(DI),AOP
1)IOC和DI
IOC:控制反转
即控制权的转移,将我们创建对象的方式反转了,以前对象的创建是由我们开发人员自己维护,包括依赖关系也是自己注入,使用了Spring之后,对象的创建以及依赖关系可以有Spring完成创建以及注入,反转控制就是反转了对象的创建方式,从我们自己创建反转给了程序创建。
DI:Dependency Injection 依赖注入
Spring这个容器中,替你管理着一系列的类,前提是你需要将这些类交给Spring容器进行管理,然后在你需要的时候,不是自己去定义,而是直接向Spring容器索取,当Spring容器知道你的需求之后,就回去它所管理的组件中进行查找,然后直接给你所需要的组件。
2)AOP 面向切面编程
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。(来自百度)
上面是比较专业的术语,接下来给一个简单的例子:
将一个习惯切成两半,那么切开的地方就是切面;而在炒菜这个过程中,锅与炉子一起合作完成这项任务,那么锅与炉子就是切面,同样的在编程中,对象与对象,方法与方法之间,模块与模块之间都是一个切面。
我们每一次炒菜炒的可能都是不同一个菜,但是都要用的锅和炉子,那么如果给每个菜都配一套锅和炉子,是不是很浪费,那么就是不管炒什么菜都是同一套锅和炉子,那么每次炒菜是不是都要去调用那一锅和炉子,那么这里的菜是不是可以看作是接口,而锅和炉子就是方法,那么我将方法直接注入到接口调用的某个地方(切点),那么此时接口就只需要关注具体的业务是实现,而不需要关心其他非该接口关注的逻辑和处理。
AOP中的名词解释
JoinPoint(连接点):目标对象中,所有可以增强的方法,就是Spring允许你通知(Advice)的地方,那可就真多了,基本上每个方法的前、后(两者都有也可),或抛出异常时都可以是连接点,Spring只支持方法。
PointCut(切入点):目标对象中,已经被增强的方法,调用这几个方法之前,之后或者抛出异常时干点什么,那么就用切入点来定义这几个方法。
Advice(通知/增强):增强方法的代码、想要的功能。
Target(目标对象):被代理的对象,被通知的对象,被增强的类对象。
Weaving(织入):将通知应用到连接点形成切入点的过程。
Proxy(代理):将通知织入到目标对象之后形成的代理对象。
aspect(切面):切入点+通知——通知(Advice)说明了干什么的内容(即方法体代码)和什么时候干(什么时候通过方法名中的before,after,around等就能知道),而切入点说明了在哪干(指定到底是哪个方法),切入点表达式等定义。
虽然现在都用Maven项目构建,但是不能忘记,使用aop需要用到的包:spring-aop+spring-aspecs+springsource.org.aopalliance+springsource.org.aspectj.weaver
关于与一个AOP的小例子:
1、准备目标对象(被代理对象,被通知对象,被增强的对象)
package com.HHJ.pojo;
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("添加用户");
}
@Override
public void delete() {
System.out.println("删除用户");
}
@Override
public void update() {
System.out.println("修改用户");
}
2、准备通知(被增强方法的代码,想要实现功能的方法代码)
package com.HHJ.pojo;
import org.aspectj.lang.ProceedingJoinPoint;
public class Myadvice {
//前置通知
public void before(){
System.out.println("前置通知");
}
//后置通知
public void afterReturning(){
System.out.println("后置通知(如果出现异常不会调用)");
}
//环绕通知
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("这是环绕通知之前的部分");
Object obj=pjp.proceed();
System.out.println("这是环绕通知之后的部分");
return obj;
}
//异常通知
public void AfterException(){
System.out.println("出现异常了");
}
//后置通知
public void after(){
System.out.println("后置通知(出现异常也调用)");
}
}
3、配置applicationContext.xml
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--1、配置目标对象-->
<bean name="userService" class="com.HHJ.pojo.UserServiceImpl"></bean>
<!--2、配置通知对象-->
<bean name="mvAdvice" class="com.HHJ.pojo.Myadvice"></bean>
<!--3、配置将通知织入目标对象-->
<aop:config>
<!--配置切入点-->
<!--
public void com.HHJ.pojo.UserServiceImpl.save();
* com.HHJ.pojo.UserServiceImpl.*(..)
*com.HHJ.pojo.*ServiceImpl.*(..)
-->
<aop:pointcut id="pc" expression="execution(* com.HHJ.pojo.*ServiceImpl.*(..))"/>
<aop:aspect ref="mvAdvice">
<!--指定名为before方法作为前置通知-->
<aop:before method="before" pointcut-ref="pc"/>
<!--指定名为afterReturning方法作为后置通知-->
<aop:after-returning method="afterReturning" pointcut-ref="pc"/>
<!--异常拦截通知-->
<aop:after-throwing method="AfterException" pointcut-ref="pc"/>
<!--环绕通知-->
<aop:around method="around" pointcut-ref="pc"/>
<!--后置通知-->
<aop:after method="after" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
</beans>
4、测试
import com.HHJ.pojo.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AdviceTest {
@Resource(name="userService")
private UserService us;
@Test
public void AdviceTest1(){
us.query();
}
}
总结通知的几种类型:
1.前置通知——目标方法运行之前调用
2.后置通知——目标方法运行之后调用(如果出现异常不调用)
3.环绕通知——目标方法之前和之后调用
4.异常拦截通知——如果出现异常,就会调用
5.后置通知——目标方法运行之后调用(无论是否出现异常都会调用)
四、Spring注入方式
1、set方式注入——值类型用value注入 引用类型ref注入
<?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">
<bean id="car" class="com.HHJ.pojo.Car">
<property name="brand" value="奔驰"></property>
<property name="crop" value="白色"></property>
<property name="maxpeed" value="120"></property>
<property name="price" value="100"></property>
</bean>
</beans>
2、构造方法注入
<?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">
<bean id="cars" class="com.HHJ.pojo.Car">
<constructor-arg name="brand" value="宝马"></constructor-arg>
<constructor-arg name="crop" value="黑色"></constructor-arg>
<constructor-arg name="maxpeed" value="150"></constructor-arg>
<constructor-arg name="price" value="120"></constructor-arg>
</bean>
</beans>
函数注入
3、p名称空间注入——实际上set注入,spring特有,为了简化<property>写法
1)xml文件中<beans>标签头部导入p命名空间
xmlns:p="http://www.springframework.org/schema/p"
2)书写格式:值类型注入——p:属性名=“值” 引用数据类型注入——p:属性名-ref="引用的<bean>name属性"
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="car1" class="com.HHJ.pojo.Car" p:brand="奇瑞" p:crop="红色" p:maxpeed="100" p:price="80">
</bean>
</beans>
4、spel注入:spring Expression Language 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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="黑色" class="com.HHJ.pojo.Car">
<!--取bean标签中name为"user"中的property为name中的value值-->
<property name="brand" value="#{car.brand}"></property>
</bean>
</beans>
spel特性:使用Bean的ID来引用Bean;调用方法和访问对象的属性;对值进行算术、关系和逻辑运算;正则表达式匹配;集合操作
集合注入
1、array数组的注入
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="cb" class="com.HHJ.pojo.CollectionBean">
<!--如果数组中只准备注入一个值的(对象),直接使用value|ref即可-->
<!--array注入,多元素注入-->
<property name="arr">
<array>
<!--array数组中值类型注入方式-->
<value>value1</value>
<value>value2</value>
<!--array数组中引用类型注入方式-->
<ref bean="car"/>
</array>
</property>
</bean>
</beans>
2、list集合注入
<!--list类型的注入-->
<property name="list">
<list>
<value>value1</value>
<value>value2</value>
<ref bean="car"/>
</list>
</property>
3、map集合的注入
<!--map类型的注入-->
<property name="list">
<map>
<entry key="mapK1" value="mapV1"></entry>
<entry key="human" value-ref="Human"></entry>
<entry key-ref="Car" value-ref="Human"></entry>
</map>
</property>
4、properties的注入
<!--properties类型的注入-->
<property name="prop">
<props>
<prop key="driverClass">com.jdbc.mysql.Driver</prop >
<prop key="username" value="root"></prop >
<prop key="password" value="123456"></prop >
</props>
</property>
五、spring管理的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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="personService" class="yanxi.service.implement.PersonServiceBean"></bean>
</beans>
类别 | 说明 |
singleton | 在SpringIOC容器中仅存在一个Bean实例,Bean以单例方式存在,默认值 |
prototype | 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()相当于执行newXxxxBean(); |
request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 |
session | 同一个HTTPSession享有同一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境 |
globalSession | 一般用于Portlet应用环境,该作用域仅适用于WebApplicationContext环境 |
六、spring管理的bean的生命周期
1.单例对象 singleton (单例)
- 创建: 在Spring 容器初始化时候,创建单例对象,如果设置了init-method属性,则在
创建对象以后调用初始化方法.
-使用: 每次调用 getBean(String id,Class cla) 时候,返回的都是同一个对象.
-销毁: 在Spring 容器关闭的时候,Spring会自动销毁单例对象,如果指定了
destory-method属性,则会在销毁之前执行 销毁 方法.
2. 多例对象: prototype(译:原型)
- 创建:在调用 getBean(String id,Class cla) 方法时候,创建对象,如果设置了 init-method 属性
则在创建对象以后嗲用初始化方法
-使用: 每次调用 getBean(String id,Class cla) 时候,返回的都是新对象
-销毁: Spring 不管!!! 也不会调用 destroy-method !!!
代码跟上面单例模式类似.
七、Spring中bean的两种方式
applicationContext和BeanFactory区别
BeanFactory接口
(1)spring的原始接口,针对原始接口的实现类功能较单一
(2)BeanFactory接口实现类的容器,特点是每次在获得对象时才会创建对象
ApplicationContext接口
(1)每次容器启动时就会创建容器中配置的所有对象
(2)提供了更多的功能
(3)从类路径下加载配置文件:ClassPathXmlApplicationContext从硬盘的绝对路径下加载配置文件:FileSystemXmlApplication