Spring
Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IoC)和面向切面(AOP)。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。
Spring核心
1.控制反转(IOC)
2.面向切面(AOP)
Spring优点
1.解耦, 简化开发: Spring就是一个工厂,可以将所有对象创建和依赖关系维护交给Spring处理
2.AOP支持: 方便实现事务处理, 权限处理, 监控操作
3.声明式事务支持: 只需通过配置就可以完成事务管理
4.提供Junit支持
5.集成其他框架
6.对JavaEE API的封装, 降低开发难度
Spring体系结构
Spring是一个分层架构, 包含的功能可分为大约20个模块.
Core Container(核心容器):
Beans: 管理Beans
Core: Spring核心
Context: 配置文件
ExpressionLanguage: SpEL表达式
AOP(切面编程)
AOP框架: Aspects
Data Access(数据库整合):
JDBC, ORM, OXM, JMS, Transaction
Web(MVC Web开发):
Web, Servlet, Portlet, Struts
Test(Junit整合)
IOC(控制反转)
IOC(控制反转): 将创建对象实例交给Spring处理, 每次从Spring工厂中获得对象
下面使用一个简单的例子说明:
目标类:
public interface UserService{
void addUser();
}
public class UserServiceImpl implements UserService {
public void addUser() {
System.out.println("ico add user");
}
}
Spring的配置文件:
配置文件名, 放置的位置都是任意的, 一般将配置文件命名为applicationContext.xml, 放置在src下
src目录下的applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--xsd约束-->
<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">
<!--
id: Spring帮你new 的那个对象名
class: 那个对象的全类名
-->
<bean id="userService" class="com.a_ioc.UserServiceImpl"></bean>
</beans>
测试代码:
public class TestIoc {
@Test
public void f1(){
//XMLPATH代表配置文件所在的位置
String XMLPATH="applicationContext.xml";
//使用ClassPathXMLApplicationContext(String path)加载指定位置的配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(XMLPATH);
//使用applicationContext的getBean获得配置文件中id为userService的对象
UserService userService = (UserService) applicationContext.getBean("userService");
userService.addUser();
}
}
测试方法中不再使用new的方式获得对象, 而是通过控制反转, 将new的行为交给Spring处理(反射机制)
DI(依赖注入)
DI依赖注入: 对实例对象的属性赋值, 一般通过set方法进行反射赋值.
对象的属性一般指另一个对象(就有依赖一说)
下面使用例子说明DI
User的Service层:
public interface UserService{
void addUser();
}
public class UserServiceImpl implements UserService {
//对UserDao对象做依赖注入, 在后面生成get/set方法
private UserDao userDao;
public void addUser() {
userDao.save();
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
User的Dao层:
public interface UserDao {
void save();
}
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("di save");
}
}
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
property标签用于依赖注入:将ref映射的对象注入到name中
name:需要注入的属性名
ref:映射到UserDao对象
-->
<bean id="userService" class="com.b_di.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<bean id="userDao" class="com.b_di.UserDaoImpl"></bean>
</beans>
测试:
测试中代码与前面一样几乎不变, 对象的生成, 对象属性的赋值都交给Spring处理, 高度解耦
public class TestDi {
@Test
public void f1(){
String XMLPATH="applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(XMLPATH);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.addUser();
}
}
Spring核心API
顶级接口: BeanFactory
用于生成任意的Bean, 采取延迟加载策略, 使用getBean才会初始化Bean
BeanFactory子接口: ApplicationContext
功能加强, 使用该接口, 当配置文件被加载的时候, 就进行对象实例化
ApplicationContext子接口:
1.ClassPathXmlApplicationContext用于加载src下的xml
运行时, xml在/WEB-INF/classes/...xml
2.FileSystemXmlApplicationContext加载指定盘符下的xml
运行时, xml在/WEB-INF/...xml
举例说明BeanFactory
public class TestBeanFactory {
@Test
public void f1(){
String XMLPATH="applicationContext.xml";
//加载完配置文件后, 并不会实例化
BeanFactory applicationContext = new XmlBeanFactory(new ClassPathResource(XMLPATH));
UserService userService = (UserService) applicationContext.getBean("userService");
userService.addUser();
}
}
调用ClassPathXmlApplicationContext(XMLPATH)加载配置文件, Spring就会调用配置文件中的配置的bean的构造函数, 而XMLBeanFactory(Resource)不会, 只是单纯加载配置文件, 只有当getBean才会调用构造函数
基于XML装配Bean
3种Bean实例化方式:
默认构造获得Bean, 使用静态工厂获得Bean, 使用实例工厂获得Bean
- 使用默认构造函数:
使用如下标签配置Bean
<bean id="..." class="..."></bean>
注: 该Bean必须有默认构造函数(使用默认构造进行反射生成)
- 静态工厂:
<bean id="" class="工厂全类名" factory-method="静态方法"></bean>
举例说明:
静态工厂:
public class StaticBeanFactory {
public static UserService newUserService(){
return new UserServiceImpl();
}
}
测试静态工厂:
public class TestStaticFactory {
@Test
public void f1(){
String XMLPATH="applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(XMLPATH);
UserService userService = (UserService) applicationContext.getBean("userService1", UserService.class);
userService.addUser();
}
}
application.xml:
<bean id="userService1" class="com.c_createBean.StaticBeanFactory" factory-method="newUserService"></bean>
- 实例工厂:
先获得工厂的实例对象, 然后通过实例对象创建想要的对象, 实例工厂提供的方法都是非静态方法
实例工厂:
public class MyBeanFactory {
public UserService newUserService(){
System.out.println("MyBeanFactory newUserService");
return new UserServiceImpl();
}
}
测试方法:
@Test
public void f1(){
String XMLPATH="applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(XMLPATH);
UserService userService = (UserService) applicationContext.getBean("userService2", UserService.class);
userService.addUser();
}
applicationContext.xml:
<!--获得工厂实例-->
<bean id="myBeanFactory" class="com.c_createBean.MyBeanFactory"></bean>
<!--通过工厂实例调用方法获得指定对象-->
<bean id="userService2" factory-bean="myBeanFactory" factory-method="newUserService"></bean>
Spring中Bean种类
普通Bean: Spring直接创建实例
<bean id="普通Bean名" class="普通Bean"></bean>
FactoryBean: 生产特定Bean的工厂,可以是静态/非静态工厂
非静态举例
<bean id="工厂对象名" class="工厂类"></bean>
<bean id="特定Bean" factory-bean="工厂对象名" factory-method="工厂方法名">
BeanFactory: 使用动态代理生成任意的Bean
Bean作用域
确定Spring创建Bean实例个数, 在bean标签中使用scope属性确定
singleton(默认值):
在Spring中只存在一个Bean实例, 单例模式.
prototype:
getBean()的时候都会new Bean(), 多例
request:
每次http请求都会创建一个Bean, 仅用于WebApplicationContext环境
session:
同一个http session共享一个Bean, 不同Session使用不同的Bean, 使用环境同上
globalSession:
用于Portlet, 环境同上
<bean id="..." class="..." scope="..."></bean>
Spring生命周期
生命周期详情:
1.instantiate bean对象实例化
2.populate properties 封装属性
3.如果Bean实现BeanNameAware 执行 setBeanName
4.如果Bean实现BeanFactoryAware 或者 ApplicationContextAware 设置工厂 setBeanFactory 或者上下文对象 setApplicationContext
5.如果存在类实现 BeanPostProcessor(后处理Bean) ,执行postProcessBeforeInitialization
6.如果Bean实现InitializingBean 执行 afterPropertiesSet
7.调用<bean init-method="init"> 指定初始化方法 init
8.如果存在类实现 BeanPostProcessor(处理Bean) ,执行postProcessAfterInitialization
9.执行业务处理
10.如果Bean实现 DisposableBean 执行 destroy
11.调用<bean destroy-method="customerDestroy"> 指定销毁方法 customerDestroy
- 初始化与销毁
在构造方法前->初始化, 容器关闭时->销毁
bean标签写法:
<bean id="..." class="..." init-method="初始化方法名" destory-method="销毁方法名">
实现初始化与销毁必须满足:
容器必须close, 此时销毁方法执行
必须是单例
下面使用一个简单例子演示:
applicationContext.xml:
<bean id="userService" class="com.e_lifeCycle.UserServiceImpl" init-method="myInit" destroy-method="myDestory"></bean>
UserServiceImp.java:
public class UserServiceImpl implements UserService {
public void addUser() {
System.out.println("lifeCycle add User");
}
public void myInit() {
System.out.println("init");
}
public void myDestory() {
System.out.println("destory");
}
}
Test:
@Test
public void f1(){
String XMLPATH="com/e_lifeCycle/applicationContext.xml";
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(XMLPATH);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.addUser();
//ApplicationContext没有close方法, 使用ClassPathXmlApplication
applicationContext.close();
}
//在执行构造完Bean对象的时候执行myInit
//在容器关闭之前执行myDestory
- BeanPostProcessor实现AOP, 完成事务处理
Bean实现BeanPostProcessor, 当Spring管理Bean的时候,
就能在初始化方法前执行PostProcessAfterInitialization(Object bean, String beanName);
在初始化方法后执行PostProcessBeforeInitialization(Object bean, String beanName);
下面举例说明:
编写BeanPostProcessor:
public class PostProcessor implements BeanPostProcessor{
/**
* 执行方法之前调用, 就意味着在初始化的时候就会调用事务处理
* 此时的事务处理是针对接口中有的方法
*/
@Override
public Object postProcessBeforeInitialization(final Object bean, String beanName)
throws BeansException {
System.out.println("方法前"+beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(final Object bean, String beanName)
throws BeansException {
System.out.println("方法后"+beanName);
return Proxy.newProxyInstance(
PostProcessor.class.getClassLoader(),
bean.getClass().getInterfaces(),
new InvocationHandler() {
//反射重写invoke, 实现AOP
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("开启事务");
Object objectInvoke = method.invoke(bean, args);
System.out.println("关闭事务");
return objectInvoke;
}
});
}
}
在applicationContext.xml中配置:
<bean class="com.e_lifeCycle.PostProcessor"></bean>
注册自定义的BeanPostProcessor
测试类:
测试类就是前面的初始化与销毁代码
运行结果:
构造
方法前userService
init
方法后userService
开启事务
lifeCycle add User
关闭事务
destory
生命周期, 事件执行流程
使用上面的lifeCycle作为例子
1.加载配置文件, 构造方法执行
2 装载属性,调用setter方法
3.通过BeanNameAware接口,获得配置文件id属性的内容:lifeUser
4.通过ApplicationContextAware接口,获得Spring容器
5. 实现BeanPostProcessor后处理,初始化前,执行postProcessBeforeInitialization方法
6.通过InitializingBean,确定属性设置完成之后执行
7.配置init-method执行自定义初始化方法
8. 实现BeanPostProcessor后处理,在自定义初始化之后,执行postProcessAfterInitialization方法
9.通过DisposableBean接口,不需要配置的销毁方法
10.配置destroy-method执行自定义销毁方法
属性依赖注入
1.通过构造方法进行装配:
在bean标签中添加constructor-arg标签进行装配.
例如:
<bean id="..." class="...">
<constructor-arg name="username" value="jack"></constructor-arg>
<constructor-arg index="0" type="java.lang.String" value="jack"></constructor-arg>
<constructor-arg name="username" ref="userService"></constructor-arg>
</bean>
name: 构造函数的参数名称
value: 为属性注入普通类型数据
index: 构造函数第几个参数
type: 构造函数参数的类型, 结合index使用
ref: 为属性注入其他Bean
注: 使用这个参数每次都是调用Bean中首次与标签中数据匹配合适构造函数
2.使用setter方法注入:
就是使用property标签指定name, value/ref值
<bean id="..." class="...">
<property name="username" value="jack"></property>
</bean>
P命名空间
主要对"setter方法依赖注入"做简化, 替换property.
在添加P命名空间:
在beans标签中添加: xmlns:p=“http://www.springframework.org/shema/p”
将原来的:
<bean id="..." class="...">
<property name="username" value="jack"></property>
</bean>
变为:
<bean id="..." class="..." p:name="username" p:value="jack"></bean>
SPEL
用于简化property中属性名, 与属性值的写法, 类似于EL表达式
<property name="属性名" value="#{表达式}"></property>
#{123}, #{'jack'} => 代表数字, 字符串
#{beanId} => 另一Bean的引用
#{beanId.propertyName} => 使用另一个beanId的属性值为当前bean的属性值赋值
#{beanId.clone()} => 使用beanId的方法为bean属性赋值
#{T().字段|方法} => 使用其他类的字段或方法为bean赋值
例:
<property name="cname" value="#{'jack'}"></property>
<property name="cname" value="#{customerId.cname.toUpperCase()}"></property>
通过另一个bean,获得属性,调用的方法
<property name="cname" value="#{customerId.cname?.toUpperCase()}"></property>
?. 代表: 如果对象不为null,将调用方法
4.集合注入:
在property标签中注入集合数据
注入数组:
<property name="属性名">
<array>
<value>A</value>
<value>B</value>
<value>C</value>
</array>
</property>
注入List/Set只需将<array>改为<list>, <set>
注入Map:
<property name="属性名">
<map>
<entry key="1" value="A"></entry>
<entry>
<key><value>2</value></key>
<value>B</value>
</entry>
上面这两种写法效果一样
</map>
</property>
注入Properties数据:
<property name="propsData">
<props>
<prop key="1">A</prop>
<prop key="2">B</prop>
</props>
</property>
基于注解装配Bean
使用注解的前提:
在 beans 标签下添加 context:component-scan标签, 扫描包下所有类的注解
<context:component-scan base-package="com.itheima.g_annotation.a_ioc"></context:component-scan>
取代 bean 标签的注解:
1. @Component("beanId")
等价于<bean id="beanId" class="...">
@Component("userServiceId")
public Class UserServiceImpl implements UserService{
//..............
}
2. 在web中, 提供3个注解用于标识三层架构, 这3个注解效果与Component一样
@Repository: dao层
@Service: service层
@Controller: web层
取代setter依赖注入的注解:
在类中对象的属性上加注解, 就能完成注入(没有setter方法也可以注入)
1.普通数据类型值: @Value("属性值")
@Value
private UserService userService;
2.引用数据类型值:
按照类型注入: @Autowired
@Autowired
private UserService userService;
按照名称注入: @Autowired@Qualifier("...") 或者 @Resource("...") 两注解效果一样
//使用service层生成的userService对象进行注入
@Autowired@Qualifier("userService")
private UserService userService;
3.生命周期: 配置init-method, destroy-method属性
初始化:@PostConstruct
@PostConstruct
public void myInit(){......}
销毁: @PreDestroy
@PreDestroy
public void myDestroy(){......}
4.作用域: 配置scope属性
@Scope("prototype")
配置多例