可以用于开发任何 Java 应用程序,但是在 Java EE 平台上构建 web 应用程序是需要扩展的。 Spring 框架的目标是使 J2EE 开发变得更容易使用,通过启用基于 POJO 编程模型来促进良好的编程实践。
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.3.2version>
dependency>
<bean id="a" class="com.blb.A" />
public class Demo {
public static void main(String[] args){
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
B b = classPathXmlApplicationContext.getBean(B.class);
}
}
控制反转IoC(Inversion of Control),是一种设计思想。没有IoC的程序中我们使用面向对象编程对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,由Spring来管理应用中所有对象的生命周期。
IOC的主要目的是为了解耦。
这是一个最简单的容器,它主要的功能是为依赖注入 (DI) 提供支持,这个容器接口在 org.springframework.beans.factory.BeanFactor 中被定义。BeanFactory 和相关的接口,比如BeanFactoryAware、DisposableBean、InitializingBean,仍旧保留在 Spring 中,主要目的是向后兼容已经存在的和那些 Spring 整合在一起的第三方框架。
在 Spring 中,有大量对 BeanFactory 接口的实现。其中,最常被使用的是 XmlBeanFactory 类。这个容器从一个 XML 文件中读取配置元数据,由这些元数据来生成一个被配置化的系统或者应用。
在资源宝贵的移动设备或者基于 applet 的应用当中, BeanFactory 会被优先选择。否则,一般使用的是 ApplicationContext,除非你有更好的理由选择 BeanFactory。
Application Context 是 BeanFactory 的子接口,也被成为 Spring 上下文。
Application Context 是 spring 中较高级的容器。和 BeanFactory 类似,它可以加载配置文件中定义的 bean,将所有的 bean 集中在一起,当有请求的时候分配 bean。 另外,它增加了企业所需要的功能,比如,从属性文件中解析文本信息和将事件传递给所指定的监听器。
ApplicationContext 包含 BeanFactory 所有的功能,一般情况下,相对于 BeanFactory,ApplicationContext 会更加优秀。当然,BeanFactory 仍可以在轻量级应用中使用,比如移动设备或者基于 applet 的应用程序。
常用的实现 | 说明 |
---|---|
FileSystemXmlApplicationContext | 该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供给构造器 XML 文件的完整路径。 |
ClassPathXmlApplicationContext | 该容器从 XML 文件中加载已被定义的 bean。在这里,你不需要提供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中搜索 bean 配置文件。 |
WebXmlApplicationContext | 该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean。 |
被称作 bean 的对象是构成应用程序的支柱也是由 Spring IoC 容器管理的。bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象。
被IOC容器管理的对象。
使用构造方法初始化。
spring.xml:
<bean id="b" class="com.tc.demo1.StaticFactory" factory-method="createB" />
StaticFactory.java:
public class StaticFactory {
public static B createB(){
// 构造前后都可以插入操作
return new B();
}
}
spring.xml:
<bean id="factory" class="com.blb.Factory" />
<bean id="b" factory-bean="factory" factory-method="createB" />
Factory.java:
public class Factory {
public B createB(){
// 构造前后都可以插入操作
return new B();
}
}
作用域 | 说明 |
---|---|
singleton | 单例模式,在IOC容器中仅存在一个实例 |
prototype | 多例模式,每次从IOC容器调用Bean时,都返回一个新的实例 |
request | 每次Http请求都会创建一个新的Bean |
session | 同一个会话共享一个Bean,不同的会话使用不同的Bean |
application | 一般用于portlet应用环境 |
后面三种作用域仅适用于WebApplicationContext环境。
子 bean 的定义继承父定义的配置数据,子定义可以根据需要重写一些值,或者添加其他值。
总的来说,Bean的继承类似于Java的继承。
如:
<bean id="testA" class="com.blb.entity.Person" >
<property name="id" value="123" />
<property name="username" value="zhangsan" />
bean>
<bean id="testB" class="com.blb.entity.Person" parent="testA">
<property name="id" value="321" />
<property name="sex" value="男" />
bean>
依赖注入就是通过IOC容器将类之间的关系在容器内部绑定,使得类与类之间进一步解耦,让类更加专注于业务本身而不用处理其他对象的生命周期。无论是IOC或者是DI其实都是为了解耦,前者是为了将类的生命周期交给IOC容器统一管理,后者处理类与类的关系,而这些动作都将在容器内完成。
B.java:
@Data
public class B {
private A a;
// 基于set方法的依赖注入
public void setA(A a) {
this.a = a;
}
public void say(){
a.say();
}
}
A.java:
@Data
public class A {
public void say(){
System.out.println("I am A!");
}
}
spring.xml:
<bean id="a" class="com.blb.A" />
<bean id="b" class="com.blb.B" >
<property name="a" ref="a">property>
bean>
B.java:
@Data
public class B {
private A aa;
// 基于构造方法的依赖注入
public B(A aa){
this.aa = aa;
}
public void say(){
aa.say();
}
}
A.java:(同Set方法注入)
spring.xml:
<bean id="a" class="com.blb.A" />
<bean id="b" class="com.blb.B" >
<constructor-arg name="aa" ref="a"/>
bean>
接口注入模式因为历史较为悠久,在很多容器中都已经得到应用。但由于其在灵活性、易用性上不如其他两种注入模式,因而已经退出舞台。
<property name="names">
<list>
<value>Ziphvalue>
<value>Joinvalue>
<value>Marryvalue>
list>
property>
<property name="countries">
<map>
<entry key="CHINA" value="中国"/>
<entry key="USA" value="美国"/>
<entry key="UK" value="英国"/>
map>
property>
<property name="phones">
<set>
<value>110value>
<value>119value>
<value>120value>
set>
property>
<property name="files">
<props>
<prop key="first">Oneprop>
<prop key="second">Twoprop>
<prop key="third">Threeprop>
props>
property>
在Spring2.5后引入了P名称空间(属性的方法),用于简化setter方法属性注入。
使用P名称空间,首先需要在applicationContext.xml中引入P名称空间的配置:
xmlns:p="http://www.springframework.org/schema/p"
使用方法:
p:< 属性名 >=“xxx” 引入常量值
p:< 属性名 >-ref=“xxx” 引用其它 Bean 对象
示例:
<bean id="employee2" class="cn.itcast.spring.e_di.Employee"
p:eid="100002" p:name="李四" p:car-ref="car"/>
使用c名称空间(构造器的注入),首先需要在applicationContext.xml中引入P名称空间的配置:
xmlns:p="http://www.springframework.org/schema/c"
(使用方法同p名称空间)
在标签里加入lazy-init=“true/false/default”,即可设置是否懒加载。
参数值为true为懒加载,即真正调用时才加载;
参数值为false为非懒加载,即启动Spring容器就创建对象。
当scope=“prototype” (多例)时,参数为default默认为懒加载:
当scope=“singleton”(单例)时,参数为default默认为非懒加载。
懒加载:对象使用时才去创建,节省资源,但不利于提前发现错误。
非懒加载:容器启动立刻创建对象,消耗资源,利于提前发现错误。
要使用自动装配只需要在标签里加上autowire="XX"的属性即可。
自动装配方式 | 说明 |
---|---|
no(默认) | 不自动装配 |
byName | 根据名称来自动装配 |
byType | 根据类型来自动装配 |
constructor | 使用构造方法来自动装配 |
autowire-candidate | 参数值为false表示该类不参与自动装配 |
在Spring3中提供了功能强大的表达式语言,简称SpringEL。SpringEL类似于OGNL和JSF的表达式语言,能够在运行时构建复杂表达式,存取对象属性、对象方法调用等操作。
表达式格式:#{SpringEL expression}
<bean id="testA" class="com.blb.entity.Person" >
<property name="id" value="123" />
bean>
<bean id="testB" class="com.blb.entity.Person">
<property name="id" value="#{testA.id}" />
bean>
<bean id="testA" class="com.blb.entity.Person" >
<property name="id" value="123" />
bean>
<bean id="testC" class="com.blb.entity.Person">
<property name="id" value="#{testA.getId()}" />
bean>
关系运算符 | 说明 |
---|---|
==、eq | 等于 |
!=、ne | 不等于 |
<、lt | 小于 |
<=、le | 小于等于 |
>、gt | 大于 |
>=、ge | 大于等于 |
<bean id="testA" class="com.blb.entity.Person" >
<property name="id" value="123" />
<property name="list">
<list>
<value>100value>
<value>20value>
list>
property>
<property name="myMap" >
<map>
<entry key="name" value="admin"/>
<entry key="age" value="100" />
map>
property>
bean>
<bean id="testD" class="com.blb.entity.Person">
<property name="value" value="#{testA.list[0]}" />
<property name="name" value="#{testA.myMap[name]}" />
bean>
使用反射+xml,实现一个IOC和DI功能(待完成)。
面向切面的编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象的编程(OOP)。OOP中模块化的关键单元是类,而在AOP中模块化是切面。切面使关注点(例如事务管理)的模块化可以跨越多种类型和对象。
Spring的关键组件之一是AOP框架。尽管Spring IoC容器不依赖于AOP(这意味着您不需要使用AOP),但AOP是对Spring IoC的补充,以提供功能强大的中间件解决方案。
AOP在Spring框架中用于:
组成部分 | 说明 |
---|---|
切面 | 要切入的模块代码 |
切入点 | 模块要切入的地方 |
通知 | 切入模块的运行时机 |
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.3.2version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjrtartifactId>
<version>1.9.6version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.6version>
dependency>
<bean id="aop" class="com.blb.test1.AOP" />
<aop:config>
<aop:aspect ref="aop" >
<aop:pointcut id="myAspect" expression="execution(* com.blb.test1.User.say())" />
<aop:after method="say" pointcut-ref="myAspect" />
aop:aspect>
aop:config>
通知的种类 | 说明 |
---|---|
before | 前置通知 |
after | 后置通知 |
around | 环绕通知 |
throwing | 异常通知(切入点发生异常才执行) |
returning | 返回通知(切入点正常返回后执行) |
around的通知的切面代码较为特殊,必须要有 ProceedingJoinPoint joinPoint 参数。如:
public void say(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕头:I am AOP!");
joinPoint.proceed();// 执行被通知的方法
System.out.println("环绕尾:I am AOP!");
}
Spring AOP使用JDK动态代理或CGLIB创建给定目标对象的代理。JDK动态代理内置在JDK中,而CGLIB是常见的开源类定义库(重新打包为spring-core
)。
如果要代理的目标对象实现至少一个接口,则使用JDK动态代理。代理了由目标类型实现的所有接口。如果目标对象未实现任何接口,则将创建CGLIB代理。
代理类中调用被代理类的方法。
指jdk中的动态代理。
public class MyProxy implements InvocationHandler {
private UserService userService; // 目标类
public MyProxy(UserService userService) { // 构造方法里传入 目标类
this.userService = userService;
}
public Object invoke(Object proxy, Method method ,Object[] args) throws Throwable {
System.out.println("调用方法前");
Object invoke = method.invoke(userService, args);
System.out.println("调用方法后");
return invoke;
}
}
测试:
public class Demo {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
MyProxy myProxy = new MyProxy(userService); // 获取动态代理类对象
UserService userService1 = (UserService) Proxy.newProxyInstance(Demo.class.getClassLoader(),
new Class[]{UserService.class}, myProxy);
String nameByID = userService1.getNameByID();
System.out.println(nameByID);
}
}
public class CglibProxy implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("调用方法前");
Object o1 = methodProxy.invokeSuper(o, objects);
System.out.println("调用方法后");
return o1;
}
}
测试:
public class Demo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(User.class);
enhancer.setCallback(new CglibProxy());
User user = (User) enhancer.create();
String s = user.say("李四");
System.out.println(s);
}
}
spring中强制使用cglib的方法:
传播特性 | 说明 |
---|---|
required | 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择 |
supports | 支持当前事务,如果当前没有事务,就以非事务方式执行 |
mandatory | 支持当前事务,如果当前没有事务,就抛出异常 |
requires_new | 新建事务,如果当前存在事务,把当前事务挂起 |
not_supported | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
never | 以非事务方式执行,如果当前存在事务,则抛出异常 |
nested | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与propagation_required类似的操作 |
示例:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
bean>
<tx:advice id="TestAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="search*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
tx:attributes>
tx:advice>
<aop:config>
<aop:advisor advice-ref="TestAdvice" pointcut="execution(* com.blb.dao.impl.*.*(..))">aop:advisor>
...
aop:config>
实现方式 | 说明 |
---|---|
硬编码事务 | 传统事务管理方式,需要自己手动提交回滚事务 |
声明式事务 | 使用spring aop来声明事务 |
注解式事务 | 使用注解来使用事务 |
常用注解 | 说明 |
---|---|
@Controller | 作用在Controller上,将其注入到容器 |
@Service | 作用在service层,将其注入到容器 |
@Repository | 作用在dao层,将其注入到容器 |
@Component | 作用在普通类,将其注入到容器 |
@Autowired | 默认先按byType,如果发现找到多个bean,则,又按照byName方式比对,如果还有多个,则报出异常。 可以手动指定按byName方式注入,使用@Qualifier。 如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) |
@Resource | 默认按 byName自动注入,如果找不到再按byType找bean,如果还是找不到则抛异常,无论按byName还是byType如果找到多个,则抛异常。 可以手动指定bean,它有2个属性分别是name和type,使用name属性,则使用byName的自动注入,而使用type属性时则使用byType自动注入。 @Resource(name=”bean名字”)或@Resource(type=”bean的class”) |
@Inject | 使用@Inject需要引用javax.inject.jar,它与Spring没有关系,是jsr330规范。 与@Autowired有互换性。 |
@Scope | 配置Bean的作用域 |
@Singleton | 在类上加上这个注解,就可以实现一个单例类 |
@Value | 用于将属性文件中的值注入到实体中 |
前四个注解:@Controller、@Service、@Repository、@Component
这些注解要配合使用