一、Spring框架概念
spring 是众多开源 java 项目中的一员,基于分层的 javaEE 应用一站式轻量级开源框架,主要核心是 Ioc(控制反转/依赖注入) 与 Aop(面向切面)两大技术,实现项目在开发过程中的轻松解耦, 提高项目的开发效率。
在项目中引入spring的好处:
·降低组件之间的耦合度,实现软件各层之间的解耦
·可以使用容器提供的众多服务,如:事务管理服务、消息服务等
·当使用容器管理事务时,开发人员就不需要手工控制事务,也不需要处理复杂的事务传播
·容器提供单例模式支持,开发人员不再需要自己编写实现代码
·容器提供了AOP技术,利用它很容易实现如权限拦截、运行期监控等功能
二、Spring源码架构
1、核心容器:spring-beans 和 spring-core 模块是 Spring 框架的核心模块,包含控制反转(Inversion of Control, IoC)和依赖注入(Dependency Injection, DI) ,核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory, 工厂模式的实现。 BeanFactory 使用控制反转(IOC) 思想将应用程序的配置和依赖性规范与实际的应用程序代码分开。
2、Spring 上下文 Spring Context: Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。 Spring 上下文包括企业服务,例如 JNDI、 EJB、电子邮件、国际化、校验和调度功能。
3、Spring-Expression 模块是统一表达式语言(unified EL)的扩展模块,可以查询、管理运行中的对象,同时也方便的可以调用对象方法、操作数组、集合等。它的语法类似于传统 EL,但提供了额外的功能,最出色的要数函数调用和简单字符串的模板函数。
4、Spring-AOP: spring-aop 是 Spring 的另一个核心模块, 在 Spring中,他是以 JVM 的动态代理技术为基础,然后设计出了一系列的Aop 横切实现,比如前置通知、返回通知、异常通知等。 通过其配置管理特性, Spring AOP 模块直接将面向切面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。
......
三、Spring容器工厂的简单实现
package com.shsxt.factory; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.XPath; import org.dom4j.io.SAXReader; import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class Factory_01 implements Factory { // Map集合用来存放id和class Map<String, Object> map = new HashMap<>(); List<Bean> beans = new ArrayList<>(); // 实例化工厂对象时就完成以下操作 public Factory_01(String fileName) { // xml解析 this.pathXml(fileName); // 实例化对象 this.instanceBean(); // 属性赋值 this.setProperty(); } // 属性赋值 private void setProperty() { try { if(null!=beans && beans.size()>0){ for(Bean bean:beans){ List<Property> properties=bean.getProperties(); if(null!=properties && properties.size()>0){ for(Property property:properties){ // 得到property的id和ref String id=property.getId(); String ref=property.getRef(); // set方法中首字母大写 id=id.toUpperCase().charAt(0)+id.substring(1); // 获取到当前class Class clz=map.get(bean.getId()).getClass(); // 找到set方法 Method method=clz.getDeclaredMethod("set"+id,map.get(ref).getClass()); method.invoke(map.get(bean.getId()),map.get(ref)); } } } } } catch (Exception e) { e.printStackTrace(); } } // 实例化 private void instanceBean() { if (null != beans && beans.size() > 0) { try { for (Bean bean : beans) { // 放到map集合中,通过key找到value,实例化该对象 map.put(bean.getId(), Class.forName(bean.getClz()).newInstance()); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } } } /** * xml解析 * * @param fileName xml文件名 */ private void pathXml(String fileName) { // 获取xml文件 URL url = this.getClass().getClassLoader().getResource(fileName); try { if (null != url) { // 获取解析器 SAXReader saxReader = new SAXReader(); // 解析xml文件,返回document对象 Document document = saxReader.read(url); // 获取xpath对象 XPath xPath = document.createXPath("beans/bean"); // 查询选择的节点,返回的是list集合 List<Element> elements = xPath.selectNodes(document); // 为空判断 if (null != elements && elements.size() > 0) { for (Element element : elements) { // 获取property xPath = document.createXPath("property"); // 查询选择的节点 List<Element> subElements = xPath.selectNodes(element); // 为空判断 List<Property> properties = null; // 实例化Bean Bean bean = new Bean(element.attributeValue("id"), element.attributeValue("class")); if (null != subElements && subElements.size() > 0) { properties = new ArrayList<>(); for (Element subElement : subElements) { // 实例化Property Property property = new Property(subElement.attributeValue("id"), subElement.attributeValue("ref")); // 将对象放到集合中 properties.add(property); } bean.setProperties(properties); } beans.add(bean); } } } } catch (DocumentException e) { e.printStackTrace(); } } @Override public Object getObj(String name) { // 通过id返回class return map.get(name); } }
四、Spring多文件的加载情况
1、ClassPathXmlApplicationContext类的构造器参数是一个可变长参数
可同时配置多个xml文件
2、import标签,将自配置文件导入总配置
四、Spring Ioc实例化bean的三种方式
1、构造器的方式实例化bean对象
通过默认构造器实例化bean对象,默认空构造器必须存在
2、静态工厂方式实例化bean
要有工厂类和静态工厂方法
通过反射调用静态工厂的静态方法,将该静态方法的返回值作为bean的实例,可以统一管理各个bean的创建
3、实例化工厂方式实例化bean
工厂类和实例化方法
工厂方法为非静态,需要配置工厂bean,并在bean中配置factory-bean和factory-method属性
1) 可用于集成其他框架的bean创建管理方法 2)能够使bean和factory的角色互换
五、Spring 依赖注入
在面向接口编程中,依赖接口可以动态传入多种实现
1、set注入
property标签,属性的set方法(不会出现循环引用问题)
name:属性名称;ref:bean对象的引用;value:给属性直接赋值(List,Set,Map,properties)
2、构造器注入
constructor-arg标签,带参构造器(会出现循环引用问题,彼此互相依赖对方导致bean无法实例化)
name:属性名称;ref:bean对象id的引用;index:属性的索引
3、静态工厂注入
4、实例化工厂注入
六、注解方式注入bean
xml配置:加入context命名空间和xsd地址
添加<context:annotation-config/>配置
@Resource (属于J2EE)
@Autowired (Spring)
常用于属性字段或set方法上
区别:
@Autowired 默认按bean的类型匹配,和@Qualifier配合使用可以修改按名称匹配
@Resource 默认按名称进行装配,可以通过name属性指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行匹配注入,如果注解写在set方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配(当name属性指定,只会按照名称装配)
注解方式注入的简单模拟:
package com.shsxt02; import com.shsxt.annotaioms.Component; import com.shsxt.annotaioms.Component02; import com.shsxt02.controller.UserController; import java.io.File; import java.lang.reflect.Field; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class Test { private static List<String> clz=new ArrayList<>(); private static Map<String,Object> map=new HashMap<>(); public static void main(String[] args){ String pkg="com.shsxt02"; // 获取标记注解的类 getClZ(pkg); // 实例化这些类 instanceClz(); // 属性赋值 property(); UserController userController= (UserController) map.get("userController"); userController.test(); } private static void property() { try { if (null!=map){ for(Map.Entry entry:map.entrySet()){ Field[] fields=entry.getValue().getClass().getDeclaredFields(); if (null!=fields){ for(Field field:fields){ Component02 component02=field.getAnnotation(Component02.class); if (null!=component02){ field.setAccessible(true); field.set(entry.getValue(),map.get(component02.value())); } } } } } } catch (IllegalAccessException e) { e.printStackTrace(); } } private static void instanceClz() { try { if (null!=clz && clz.size()>0){ for(String cls:clz){ //System.out.println(str); cls=cls.replace(".class",""); //System.out.println(cls); Component component=Class.forName(cls).getAnnotation(Component.class); if (null!=component){ //System.out.println(cls); String id= getId(cls); Object obj=Class.forName(cls).newInstance(); map.put(id,obj); } } } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } } private static String getId(String cls) { cls=cls.substring(cls.lastIndexOf(".")+1); cls= cls.toLowerCase().charAt(0)+cls.substring(1); return cls; } private static void getClZ(String pkg) { // 获取url URL url=Thread.currentThread().getContextClassLoader().getResource(repStr(pkg)); String urlPath=url.getFile(); //System.out.println(urlPath); String[] subFileStrs=new File(urlPath).list(); for(String str:subFileStrs){ String subFilePath=urlPath+"/"+str; //System.out.println(subFilePath); File subFile=new File(subFilePath); if (subFile.isDirectory()){ //System.out.println(pkg+"."+subFile.getName()); getClZ(pkg+"."+subFile.getName()); }else{ clz.add(pkg+"."+subFile.getName()); } } } private static String repStr(String str){ str=str.replace(".","/"); return str; } }
七、Spring IOC 容器自动扫描管理bean
xml配置<context:component-scan base-package=""/>
建议的注解
Dao层:@Repository
Service层:@Service
控制层:@Controller
不明确:@Component
八、Bean的作用域问题(scope)
1、singleton作用域(单例,默认)
lazy-init 懒加载 默认为false
如果等于true时,spring容器启动的时候不会去实例化这个bean,而是在程序调用时才会去实例化
在启动情况下实例化所有singleton的bean对象并缓存与容器中单例的好处:
1、提前发现潜在的配置问题
2、bean对象存在于缓存中,使用时不用再实例化bean,提高执行性能
无状态对象适合做单例bean对象(无可变的成员变量)
2、prototype作用域(原型)
每次向Spring容器请求获取Bean都返回一个全新的Bean,相对于“singleton”来说就是不缓存Bean。IOC不会维护该对象
3、Web应用中的作用域(request、session、globalsession)
request作用域:每一次请求
session:当前会话
globlasession:同session(Portlet环境)