1.1 描述
在企业级开发中,spring框架应用非常广。为了让已经学习过spring框架同学,可以更深入的理解和应用spring,本文将通过自定义spring,更佳系统的阐述spring核心:IoC、AOP。
IoC(Inversion of Control)控制反转:将对象的创建权交与spring框架,及将创建权反转给spring框架。IoC主要解决计算机程序的耦合问题。
AOP(Aspect Oriented Programming)面向切面编程:通过运行期动态代理实现程序功能的统一维护的一种技术。
1.2 分析
l 如果要实现自定义spring,可以将器拆分成多个功能实现。
l 阶段一:编写配置文件,服务器tomcat启动时,加载配置文件
l 阶段二:使用Jsoup解析xml,并封装到指定的JavaBean中
l 阶段三:编写工厂类用于创建指定bean,并完成property注入
l 阶段四:使用@Transactional进行事务管理
1.3 搭建环境
1.3.1 javabean
| public class User {
private Integer uid;
private String username;
private String password; |
1.3.2 dao
| public interface UserDao {
/**
* 保存
* @param user
*/
public void save(User user);
} |
| public class UserDaoImpl implements UserDao {
@Override
public void save(User user) {
//TODO 暂时只打印
System.out.println(user);
}
} |
1.3.3 service
| public interface UserService {
/**
* 注册
* @param user
*/
public void register(User user);
} |
| public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void register(User user) {
this.userDao.save(user);
}
} |
1.4 阶段一:编写配置文件,服务器启动加载
1.4.1 xml配置文件
l 在src下添加“applicationContext.xml”,并将dao和service配置到xml文件中。想学习交流HashMap,nginx、dubbo、Spring MVC,分布式、高性能高可用、MySQL,redis、jvm、多线程、netty、kafka、的加尉xin(同英):1253431195 扩列获取java进阶资料学习,无工作经验不要加哦!
l 使用标签配置一个实现类
class: 配置实现类的全限定名称
id: 进行唯一命名,用于提供给程序获得
l 使用配置javabean属性的注入
name: 配置的service的属性名
ref: 配置其他bean对象的引用
| <?xml version=*"1.0"* encoding=*"UTF-8"*?>
<bean id=“userDaoId” class=“cn.itcast.demo.dao.impl.UserDaoImpl”>
<bean id=“userServiceId” class=“cn.itcast.demo.service.impl.UserServiceImpl”>
<property name=“userDao” ref=“userDaoId”>
|
1.4.2 加载配置文件
l tomcat启动时,加载配置文件方式总结:
1.编写Servlet,配置servlet,并添加,在init(ServletConfig)初始化方式中加载。
2.编写Filter,配置filter,在init(FilterConfig)初始化方法中加载
3.编写Listener,实现接口ServletContext,配置listener,在contextInitialized(ServletContextEvent sce)方法中加载。
l spring采用listener方案
1.提供实现类ContextLoaderListener
2.编写全局初始化参数contextConfigLocation,用于确定xml位置
classpath:applicationContext.xml 加载类路径下的xml文件
applicationContext.xml 加载WEB-INF目录的配置文件
l xml配置
|
contextConfigLocation
classpath:applicationContext.xml
cn.itcast.myspring.listener.ContextLoaderListener
|
l 实现类
| public class ContextLoaderListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// 0 获得ServletContext对象应用
ServletContext context = sce.getServletContext();
// 1 加载配置
String config = context.getInitParameter(“contextConfigLocation”);
if(config == null){ //默认配置文件位置
config= “applicationContext.xml”;
}
InputStream xmlIs = null;
// 2 处理路径不同情况
// * classpath:applicationContext.xml --> 表示 src/applicationContext.xml
// * applicationContext.xml --> 表示 /WEB-INF/applicationContext.xml
if (config.startsWith(“classpath:”)) { // 2.1 加载 类路径 (classpath、src)下的xml
xmlIs = ContextLoaderListener.class.getClassLoader().getResourceAsStream(config.substring(“classpath:”.length()));
} else { //2.2 加载/WEB-INF/目录下的资源
xmlIs = context.getResourceAsStream("/WEB-INF/" + config);
}
//2.3 配置文件必须存在,否则抛异常
if(xmlIs == null){
throw new RuntimeException(“资源文件[”+config+"]没有找到");
}
//TODO 3 解析配置
if (xmlIs != null) {
System.out.println(xmlIs);
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
} |
1.5 阶段二:解析xml,并封装到指定javabean中
1.提供Property类,用于封装<property name=" " ref=" ">
2.提供Bean类,用于封装<bean id=" " class=" ">
一个 标签体中可以配置多个需要一个容器存放,没有顺序要求,且不能重复,选择Set
3.提供BeanFactory类,并在类同提供容器存放多个Bean 类,为了方便获取使用Map。想学习交流HashMap,nginx、dubbo、Spring MVC,分布式、高性能高可用、MySQL,redis、jvm、多线程、netty、kafka、的加尉xin(同英):1253431195 扩列获取java进阶资料学习,无工作经验不要加哦!
1.5.1 property javabean
| /**
* 用于封装 <property name=“userDao” ref=“userDaoId”>
*/
public class Property {
//属性名称
private String name;
//另一个bean引用名
private String ref;
public Property(String name, String ref) {
super();
this.name = name;
this.ref = ref;
} |
1.5.2 bean javabean
| public class Bean {
//bean名称
private String beanId;
//bean的实现类
private String beanClass;
//取值:singleton 单例,prototype 原型(多例)【扩展】
private String beanType;
//所有的property
private Set propSet = new HashSet();
public Bean(String beanId, String beanClass) {
super();
this.beanId = beanId;
this.beanClass = beanClass;
} |
1.5.3 BeanFactory 工厂模式类
| public class BeanFactory {
//工厂模式
private static BeanFactory factory = new BeanFactory();
private BeanFactory(){
}
/**
* 获得工厂实例
* @author lt
* @return
*/
public static BeanFactory getInstance() {
return factory;
} |
1.5.4 BeanFactory 提供Map 缓存
| //缓存所有的Bean/
//bean数据缓存集合 ,key:bean名称 ,value:bean封装对象
private static Map<String, Bean> beanData;// = new HashMap<String, String>();
static{
// 从配置文件中获得相应的数据 1.properties 2.xml
//beanData.put(“userDao”, “com.itheima.ebs.service.impl.BusinessServiceImpl”);
}
public static void setBeanData(Map<String, Bean> beanData) {
BeanFactory.beanData = beanData;
} |
1.5.5 修改Listener解析xml
l 使用Jsoup解析,导入jar包
l 修改contextInitialized 方法
| //TODO 3 解析配置
if (xmlIs != null) {
//3.1解析
Map<String, Bean> data = parserBeanXml(xmlIs);
//3.2 将解析结果放置到工厂中
BeanFactory.setBeanData(data);
} |
l 解析方法parserBeanXml(InputStream)
| /**
* 将数据解析成bean
* @param xmlIs
* @return
*/
public static Map<String, Bean> parserBeanXml(InputStream xmlIs) {
try {
//0提供缓冲区域
Map<String, Bean> data = new HashMap<String, Bean>();
//1解析文件,并获得Document
Document document = Jsoup.parse(xmlIs, “UTF-8”, “”);
//2 获得所有的bean元素
Elements allBeanElement = document.getElementsByTag(“bean”);
//3遍历
for (Element beanElement : allBeanElement) {
//5 将解析的结果封装到bean中
// 5.1 bean名称
String beanId = beanElement.attr(“id”);
// 5.2 bean实现类
String beanClass = beanElement.attr(“class”);
// 5.3 封装到Bean对象
Bean bean = new Bean(beanId,beanClass);
// 6 获得所有的子元素 property
Elements allPropertyElement = beanElement.children();
for (Element propertyElement : allPropertyElement) {
String propName = propertyElement.attr(“name”);
String propRef = propertyElement.attr(“ref”);
Property property = new Property(propName, propRef);
// 6.1 将属性追加到bean中
bean.getPropSet().add(property);
}
data.put(beanId, bean);
}
return data;
} catch (Exception e) {
throw new RuntimeException(e);
}
} |