和持久层Srping JDBC以及事务管理等。
spring是一个开源框架,为了解决企业应用开发的复杂性而创建的,但现在不止应用于企业应用。
同时是一个轻量级的控制反转ioc和面向切面编程的容器框架
轻量:从大小与开销对于spring都是轻量的
通过控制反转ioc的技术达到松耦合
提供面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统服务进行内聚性的开发
包含并管理应用对象的配置和生命周期,这个意义是容器
将简单的组件配置组合成复杂的应用,这个意义是框架
框架:框架就是定制一套规范或者规则(思想),大家在该规范或思想下进行工作,或者说
使用别人打好的舞台,你来做表演
框架于类库的区别
框架一般是封装了逻辑的,高内聚的,类库则是松散的工具集合
框架一般似乎专注于某一领域,类库则是更通用的
spring带来了复杂的JavaEE的春天
spring源码设计精妙、结构清晰,研究源码可以快速提升Java技术水平和开发应用水平
将对象的依赖交给配置文件来配置(配置文件的名字是可以任意的,不过一般写一个比较规范的名字),这里使用IOC特性对类中的属性进行初始化
使用junit来进行测试单元测试(注意:单元测试一些老的版本可能会存在bug,如calssNotFound...,建议下载新的junit版本)
User.java 用户bean类
package com.xxx.spring.ioc.bean;
import java.io.Serializable;
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private int id; //用户编号
private String name; //用户名
private int age; //用户年龄
private String gender; //用户性别
public User() {}
public User(int id, String name, int age, String gender) {
super();
this.id = id;
this.name = name;
this.age = age;
this.gender = gender;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", age=" + age
+ ", gender=" + gender + "]";
}
}
Spring中bean的配置:
这里说的Spring中的Bean概念,跟我们写JavaBean类不是一个概念,Spring中所有配置在xml中或使用spring来初始化的都叫Bean(dao,service,javaBean,Controller...)
IOC控制反转,控制权的转移,应用程序本身不负责依赖对象的创建和维护,而是有外部容器的创建和维护
(就像我们需要房子,不是自己去画图纸,建房子而是去请开发商去做,或房屋中介住房)
什么被反转了呢——————》获的对象的过程被反转了,依赖注入
set.xml配置文件初始化User.java中的相关属性,可以使用junit对其进行单元测试
测试:
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import com.briup.spring.aop.bean.AwareTest;
import com.briup.spring.ioc.UserService;
import com.briup.spring.ioc.bean.Car;
import com.briup.spring.ioc.bean.Coll;
import com.briup.spring.ioc.bean.Life;
import com.briup.spring.ioc.bean.Student;
import com.briup.spring.ioc.bean.Teacher;
import com.briup.spring.ioc.bean.User;
public class SpringTest {
@BeforeClass
public static void setUpBeforeClass() throws Exception {
System.out.println("BeforeClass 标注的方法 会最先先被执行");
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
System.out.println("AfterClass 标注的方法 会最后执行");
}
@Test
public void test() {
System.out.println("test");
//路经比较特殊
BeanFactory factory = new ClassPathXmlApplicationContext("com/xx/spring/chap1/ioc.xml");
UserService service = (UserService) factory.getBean("service");
service.getUserDao().save();
}
@Test
public void test2() {
BeanFactory factory = new ClassPathXmlApplicationContext("com/xxx/spring/chap1/set.xml");
//User user = (User) factory.getBean("user");
//User user = (User) factory.getBean("user",User.class);
//User user = (User) factory.getBean(User.class); //只有唯一的bean的时候才使用这种方式
//System.out.println(user);
System.out.println(factory.getType("user")); //获取user实例的类型
User user = (User) factory.getBean("user");
User user2 = (User) factory.getBean("user");
System.out.println(user == user2);//true -- 单例 --这是可以控制的在配置文件中 bean scope="prototype"-->会变成原型模式 这时结果会是false
System.out.println(factory.isPrototype("user"));//是否为原型 false
System.out.println(factory.isSingleton("user"));//是否为单例 true
System.out.println(factory.isTypeMatch("user", User.class));//判断 user实例是否为这种类型 true
String[] str = factory.getAliases("user"); //获取别名
for(int i=0;i
上边的案例factory.getBean("user",User.class);第一参数是set.xml文件中对应bean的name值或id值.
System.out.println(user == user2);//true
上边返回true,说明默认的是单利模式,可以通过scope改变其范围为scope="prototype"变为原型模式,这样每次初始化bean对象的时候,都会返回一个新的。
Bean容器的初始化
两个基础包:
org.springframework.beans
org.springframework.context
BeanFactory提供配置结构和基本功能,加载并初始化Bean
ApplicationContext保存了Bean对象并在spring中被广泛使用
集中常用的使用场景:
常用的文件初始化方式:
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("F:/workspace/appcontext.xml");
ApplicationContext ac = new ClassPathXmlApplicationContext("com/xxx/spring/chap1/coll.xml");
BeanFactory factory = new ClassPathXmlApplicationContext("com/xxxspring/chap1/ioc.xml");
在webapp中的我们一般配置到web.xml文件中
1.
contextConfigLocation
classpath:action.xml,classpath:dao.xml,classpath:service.xml
org.springframework.web.context.ContextLoaderListener
2.load-on-startup标签指定启动顺序,1为指在启动服务器的时候初始化容器
org.springframework.web.context.ContextLoaderListener
remoting
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring-remoting-servlet.xml
1
a.设置值注入
b.构造注入
设置值注入案例:
基本类型的注入: 通过
引用类型的注入:
容器初始化并保存到容器中
构造注入
顾名思义,使用构造器对对象的初始化注入对应的值,实现方式有如下3种
public class Teacher implements Serializable{
private static final long serialVersionUID = 1L;
private int id;
private String name;
private String gender;
public Teacher(int id, String name, String gender) {
super();
this.id = id;
this.name = name;
this.gender = gender;
}
@Override
public String toString() {
return "Teacher [id=" + id + ", name=" + name + ", gender=" + gender
+ "]";
}
}
@Test
public void test3() throws Exception {
ApplicationContext ac = new ClassPathXmlApplicationContext("com/xxx/spring/chap1/constructor.xml");
Teacher teacher = (Teacher) ac.getBean("teacher");
System.out.println(teacher);//Teacher [id=1, name=tom, gender=male]
}
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
public class Life implements BeanNameAware,BeanFactoryAware{
private String name;
public Life(){//一加载就会调到用
System.out.println("调用无参构造器");
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("调用setName方法");
this.name = name;
}
public void myInit() {
System.out.println("调用myInit方法");
}
public void myDestory(){
System.out.println("调用myDestory方法");
}
@Override
public void setBeanFactory(BeanFactory arg0) throws BeansException {
System.out.println("调用setBeanFactory方法");
}
@Override
public void setBeanName(String arg0) {
System.out.println("调用setBeanName方法");
}
}
测试:
@Test
public void life(){//springBean的生命周期
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("com/xxx/spring/chap2/life.xml");
Life life = ac.getBean("life",Life.class);
System.out.println(life);
ac.destroy();
}
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class AwareTest implements ApplicationContextAware,BeanNameAware{
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println(applicationContext.getBean(AwareTest.class));
}
@Override
public void setBeanName(String beanName) {
System.out.println(beanName);
}
}
@Test
public void AwareTest(){
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("com/xxx/spring/chap1/aware.xml");
AwareTest awareTest = ac.getBean("applicationAawareTest",AwareTest.class);
System.out.println(awareTest);
}
Resources针对文件的统一接口,用于操作本地资源或网络资源,或其他
-UrlResource:URL对应的资源,根据一个URL地址既可以构建
-ClassPathResource:获取类路径下的资源文件
-FileSystemResource:获取文件系统中的资源文件
-ServletContextResource:ServletContext封装资源,用于访问ServletContext环境下的资源
-InputStreamResource:针对输入流封装的资源
-ByteArrayResource:针对字节数组封装的资源
ResourceLoader
-所用的application context 实现了ResourceLoader接口
spring中ResourceLoader定义如下:
public interface ResourceLoader{
Resource getResource(String location);
}
getResource中location的写法有如下几种
prefix前缀 案例 说明
classpath: classpath:com/briup/spring/chap2/life.xml 从classpath中加载
file: file:/data/life.xml用URL从文件系统中加载
http: http://myserver/logoo.png通过URL从网络加载
(none) /spring/chap2/life.xml 这种相对路径的写法依赖于ApplicationContext
spring中的使用
Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("file:some/resource/path/myTemplate.txt");
案例:
resources.xml
由于spring中所有的applicationcontext实现了ContextLoader接口, 所以我们实现applicationContext即有了ResourceLoader的能力
下边:classpath:在eclipse中会加载src下的config.txt文件
import java.io.IOException;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.Resource;
//所有的ApplicationContext实现了ResourceLoader接口
public class ResourceTest implements ApplicationContextAware{
private ApplicationContext ApplicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ApplicationContext = applicationContext;
}
public void resource() throws IOException{
//Resource resource = ApplicationContext.getResource("config.txt");//默认为classpath
//Resource resource = ApplicationContext.getResource("classpath:config.txt");
//Resource resource = ApplicationContext.getResource("file:D:\\workspace\\xnxy_spring\\src\\config.txt");
Resource resource = ApplicationContext.getResource("url:http://repo.springsource.org/libs-release-local/org/springframework/spring/3.2.4.RELEASE/spring-framework-3.2.4.RELEASE-dist.zip");
System.out.println(resource.getFilename());//获取文件名
System.out.println(resource.contentLength()); //获取文件长度
System.out.println(resource.getInputStream());//获取输入流
}
}
测试:
@Test
public void ResourceTest(){
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("com/briup/spring/chap1/resources.xml");
ResourceTest resourceTest = ac.getBean("resourcetest",ResourceTest.class);
try {
resourceTest.resource();
} catch (IOException e) {
e.printStackTrace();
}
}
我们还可以使用如下标签,context:annotation-config,不过context:component-scan包含context:annotation-config的全部功能,通常使用前者后,不再使用后者context:component-scan一般用于基于类的注解(包括成员变量或成员方法的注解),但是context:annotation-config只能在完成bean注册后,去处理bean类中的成员变量或成员方法的注解.
}
Dao
//设置beanName默认使用类名,首字母小写作为beanName
@Repository
public class MovieFinderImpl implements MovieFinder{
}
作用域的注解Scope
通常情况下自动查找的Spring组件,其Scope是singleton,其Spring2.5提供了Scope的注解 @Scope
@Scope("prototype") //括号中指定Scope的范围,默认
@Repository
public class MovieFinderImpl implements MovieFinder{
}
也可以自定义scope策略,实现ScopeMetadataResolver接口并提供一无参数的构造器
//由于不知道其作用于DAO或Service所以使用通用注解,如果知道具体作用在那层,我们一班使用更具体注解方式如@Service,@Repository等
//@Component -->默认使用类名小写作为bean的name
@Scope("prototype") //括号中为Scope的范围,这里设置为原型模式
@Component("beanAnnotation")
public class BeanAnnotation {
public void say(String arg){
System.out.println("BeanAnnotation: "+arg);
}
}
测试:
@Test
public void testAnnotation(){
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("com/xxx/spring/chap4/annotation.xml");
//@Component没有value值的话,默认使用类名首字母小写作为bean的id,指定value以value值为准作为id
BeanAnnotation beanAnnotation1 = ac.getBean("beanAnnotation",BeanAnnotation.class);
BeanAnnotation beanAnnotation2 = ac.getBean("beanAnnotation",BeanAnnotation.class);
System.out.println(beanAnnotation1);
System.out.println(beanAnnotation2);
//结果
//com.xxx.spring.aop.bean.annotation.BeanAnnotation@1598d5f
//com.xxx.spring.aop.bean.annotation.BeanAnnotation@505fd8
}
这个注解相当于我们之前在xml文件中配置的autowire="constructor/byName/byType",只不过我们这里使用@Autowired方式注解方式,且默认是通过类型判断,意思就是不使用byName,和construtor。通过@Autowired注解,spring会自动去容器中查找对应的类型,注入到该属性中,且bean类中,使用@Autowired注解其属性,我们可以不用提供getter,setter方法
使用@Autowired
@Autowried对属性进行注解的时候,我们可以省略getter,setter方法,通过对应的bean的类型,对属性值注入
@Autowried对seter方法进行注解的时候,可以注入对应的值
@Autowried对构造器进行注解的时候,可以通过类型找到对应的bean注入
@Autowried可以将 @Autowried为”传统“的setter方法代替 @Required
@Autowried自动注入,会去容器中按照类型查找对应的bean注入
案例:
setter中使用
pulic class simpleMovieLister{
private MoiveFinder movieFinder;
@Autowried
public void setMovieFinder(MovieFinder movieFinder){
this.movieFinder = movieFinder;
}
//..
}
属性和构造器中使用
pulic class MovieRreCommender{
成员变量中
@Autowried
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
//构造器中
@Autowried
public MovieRreCommender(CustomerPreferenceDao customerPreferenceDao){
this.CustomerPreferenceDao = CustomerPreferenceDao;
}
}
@Autowired(requried=false), @Qualifie("beanName)指定@Autowired注入那个bean实例
默认情况下,如果因找不到合适的bean将会导致autowiring失败抛出异常,可以通过下边
这种方式避免
pulic class simpleMovieLister{
private MoiveFinder movieFinder;
@Autowried(requried=false)//指明该属性不是必须的,找不到的情况下不会抛出异常
public void setMovieFinder(MovieFinder movieFinder){
this.movieFinder = movieFinder;
}
//..
}
提示:每一类中只能有一个构造器被标记为requried=ture建议将 @Autowired的必要属性时,使用 @Requried注解
public class MovieRecommander{
@Autowired
@Qualifier("beanName")
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
//@Qualifier也可以实现参数的注入
public void prepare(@Qualifier("beanName")CustomerPreferenceDao customerPreferenceDao){
this.customerPreferenceDao = customerPreferenceDao;
}
}
比如说:BeanFacotry,ApplicationContext,Environment,ResourceLoader,ApplicaiontEventPublisher, MessageSource等
pulic class simpleMovieLister{
@Autowired
private AplicationContext context;
public simpleMovieLister(){}
}
public interface BeanInterface {
}
实现类1:
@Order(1)
@Component
public class BeanImplOne implements BeanInterface {
}
@Order(2) //Order排序注解只对list,或数组集合有效括号里边是排序顺序
@Component
public class BeanImplTwo implements BeanInterface {
}
调用类:
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class BeanInvoker {
@Autowired //该注解会将所有的BeanInterface类型的bean注入到该list中
//如果bean有 @Order注解可以实现排序
private List list;
//该注解会将所有的BeanInterface类型的bean注入到该map中,key值为bean的名字
//是String类型,map类型无排序可言
@Autowired
private Map map;
public void print(){
if(list != null && 0 != list.size()){
System.out.println("list...");
for(BeanInterface beanInterface:list){
System.out.println(beanInterface.getClass().getName());
}
}
if(map != null && 0 != map.size()){
System.out.println("map...");
Set> entrySet = map.entrySet();
for(Entry entry: entrySet){
System.out.println(entry.getKey()+"--"+entry.getValue().getClass().getName());
}
}
}
}
测试类:
@Test
public void testAutowired2(){
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("com/xxx/spring/chap4/annotation.xml");
BeanInvoker beanInvoker = (BeanInvoker) ac.getBean("beanInvoker");
beanInvoker.print();
}
@Configuration //相当于配置文件
public class Appconfig{
@Bean("myservice")//假如bean的name属性没有指定名字的话,注入的是id为方法名的bean,一般我们指定name属性不容易出错
public Myservice myservice(){
return new MyServiceImpl();
}
/*
对比基于XML文件中的配置效果类似
*/
}
public class Foo{
public void init(){
}
}
public class Bar{
public void cleanup(){
}
}
@Configuration
public class Appconfig{
@Bean(name="life") //定义bean的name
public Life life(){
return new Life();
}
@Bean(initMethod="init") //在初始化Foo的时候,会调用Foo.java中的init方法
public Foo foo(){
return new Foo();
}
@Bean(destoryMethod=“cleanup”) //在销毁Bar的时候会调用Bar.java中的cleanup中的方法
public Bar bar(){
return new Bar();
}
}