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: 管理BeansCore: 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 {@Testpublic 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 {@Testpublic void f1(){String XMLPATH="applicationContext.xml";ApplicationContext applicationContext = new ClassPathXmlApplicationContext(XMLPATH);UserService userService = (UserService) applicationContext.getBean("userService");userService.addUser();}
}
Spring核心API
顶级接口: BeanFactory用于生成任意的Bean, 采取延迟加载策略, 使用getBean才会初始化BeanBeanFactory子接口: ApplicationContext功能加强, 使用该接口, 当配置文件被加载的时候, 就进行对象实例化ApplicationContext子接口:1.ClassPathXmlApplicationContext用于加载src下的xml运行时, xml在/WEB-INF/classes/...xml2.FileSystemXmlApplicationContext加载指定盘符下的xml运行时, xml在/WEB-INF/...xml
举例说明BeanFactory
public class TestBeanFactory {@Testpublic 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 {@Testpublic 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();}
}测试方法:
@Testpublic 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:@Testpublic void f1(){String XMLPATH="com/e_lifeCycle/applicationContext.xml";ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(XMLPATH);UserService userService = (UserService) applicationContext.getBean("userService");userService.addUser();//ApplicationContext没有close方法, 使用ClassPathXmlApplicationapplicationContext.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{/*** 执行方法之前调用, 就意味着在初始化的时候就会调用事务处理* 此时的事务处理是针对接口中有的方法*/@Overridepublic Object postProcessBeforeInitialization(final Object bean, String beanName)throws BeansException {System.out.println("方法前"+beanName);return bean;}@Overridepublic 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@Overridepublic 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测试类:
测试类就是前面的初始化与销毁代码
运行结果:构造方法前userServiceinit方法后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("属性值")@Valueprivate UserService userService;2.引用数据类型值: 按照类型注入: @Autowired@Autowiredprivate UserService userService;按照名称注入: @Autowired@Qualifier("...") 或者 @Resource("...") 两注解效果一样//使用service层生成的userService对象进行注入@Autowired@Qualifier("userService")private UserService userService;3.生命周期: 配置init-method, destroy-method属性初始化:@PostConstruct@PostConstructpublic void myInit(){......}销毁: @PreDestroy@PreDestroypublic void myDestroy(){......}4.作用域: 配置scope属性@Scope("prototype")配置多例