1. 模拟实现Spring.
为什么要使用Spring呢?先考虑下最常用的访问数据库的框架:
1)POJO层有一个类User
2)Service层有一个类UserService,里面有一个成员变量User u,一个addUser(User u)方法
3)DAO层用来访问数据库(数据库可以在MySQL和Oracle之间任意切换):有一个UserDao接口(里面有一个addUser(User u)方法),该接口下面有两个实现类UserDaoMySQL, UserDaoOracle, 分别实现了addUser方法。
这样,在UserService中调用DAO层来访问数据库的时候,需要在addUser方法中new一个具体实现的DAO. 如:UserDao dao = new UserDaoMySQL();
但当我们想要切换数据库到Oracle的时候,我们需要将这句代码改成UserDao dao = new UserDaoOracle();
这样代码耦合度高,且需要重新编译class.
Spring有效的解决了上述问题:将变化的部分放到XML文件里面,然后对XML文件做解析(通过ClassPathXmlApplicationContext类),这样可以实现:
1)动态为UserDao user实例化对象(通过反射来做到)
2)动态为UserService的成员变量User u赋值(通过反射来做到)
Spring的模拟实现:
1) 写一个beans.xml: 将变化的部分/需要动态实例化/需要依赖注入的部分放入XML中。
<beans>
<!-- userDaoImpl表示UserDaoImplMySQL的instance -->
<bean id="userDaoImpl" class="source.dao.impl.UserDaoImplOracle" />
<!-- userService表示UserService的instance -->
<bean id="userService" class="source.service.UserService" >
<!-- 把userDaoImpl赋值给UserService类的成员变量userDao -->
<property name="userDao" ref="userDaoImpl"/>
</bean>
</beans>
2) ClassPathXmlApplicationContext.java: 用于解析beans.xml
package source.spring;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
/*
* 用于解析spring中的beans.xml:将xml的内容解析后存到名为beans的map中
*/
public class ClassPathXmlApplicationContext implements BeanFactory
{
private Map<String, Object> beans = new HashMap<String, Object>();
// IOC Inverse of Control DI Dependency Injection
public ClassPathXmlApplicationContext () throws Exception
{
SAXBuilder sb = new SAXBuilder();
InputStream file = new FileInputStream("src/source/beans.xml");
Document doc = sb.build(file); // 构造文档对象
Element root = doc.getRootElement(); // 获取根元素HD
List list = root.getChildren("bean");// 取名字为disk的所有元素
for (int i = 0; i < list.size(); i++) {
Element element = (Element)list.get(i);
String id = element.getAttributeValue("id");
String clazz = element.getAttributeValue("class");
Object o = Class.forName(clazz).newInstance();
System.out.println(id);
System.out.println(clazz);
beans.put(id, o);
for (Element propertyElement : (List<Element>)element.getChildren("property"))
{
String name = propertyElement.getAttributeValue("name"); // userDAO
String bean = propertyElement.getAttributeValue("ref"); // u
Object beanObject = beans.get(bean);// UserDAOImpl instance
String methodName = "set" + name.substring(0, 1).toUpperCase()
+ name.substring(1);
System.out.println("method name = " + methodName);
Method m = o.getClass().getMethod(
methodName,
beanObject.getClass().getInterfaces()[0]);
m.invoke(o, beanObject);
}
}
}
@Override
public Object getBean (String id)
{
return beans.get(id);
}
}
3)UserService.java: 把UserDao作为成员变量,并提供set,get方法
public class UserService
{
UserDAO userDao;
public UserDAO getUserDao ()
{
return userDao;
}
public void setUserDao (UserDAO userDao)
{
this.userDao = userDao;
}
public void saveUser(User u) {
userDao.addUser(u);
}
}
4) JUnit测试类UserServiceTest.java:
public class UserServiceTest
{
@Test
public void test () throws Exception
{
BeanFactory factory = new ClassPathXmlApplicationContext(); //解析xml, 通过反射做依赖注入。
UserService service = (UserService)factory.getBean("userService");
User u = new User();
service.saveUser(u);
}
}
这样,在需要切换数据库的时候,只需要修改beans.xml即可。且动态为UserService.java的成员变量UserDao实例化对象。
2. 什么是依赖注入(DI)/控制反转(IOC)?
1)依赖注入:对于UserService的成员变量userDao, 它是依赖于容器给它“注入”对象,所以叫依赖注入。
2)控制反转:对于UserService的成员变量userDao,原来是由代码做实例化,如UserDao userDao = new UserDaoImplMySQL(); 现在是由容易来控制实例化的过程,所以叫控制反转。
3. Annotation.
1)@Required:This annotation simply indicates that the affected bean property must be populated at configuration time. 即@Required修饰的setter方法必须系统初始化的时候被注入,否则报错。
2) @resource: 默认是byType
如果要指定byName, 可以这样用:
@Resource(name="userDaoImpl")
public void setUserDao (UserDAO userDao)
{
this.userDao = userDao;
}
3) @Autowired: 在setter方法上面修饰,会自动注入,默认为byType.
@Autowired 和@Resource 的区别?
1. @Autowired属于Spring的;@Resource为JSR-250标准的注释,属于J2EE的。
2. @Autowired默认按类型装配,默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,例如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用:
@Autowired()
@Qualifier("baseDao")
private BaseDao baseDao;
3. Resource,默认安装名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
@Resource(name="userDaoImpl")
public void setUserDao (UserDAO userDao)
注:@Resource(name="userDaoImpl") 等价于:
@Autowired()
@Qualifier("userDaoImpl")
4. annotation与xml的优缺点比较:
1) annotation比xml更简单直观。
2) 但annotation与java源代码紧耦合,如果要修改注入规则,则需要修改java代码,不如xml解耦性更好
5. AOP前瞻:为什么说组合比继承更灵活?
如果要在userDaoImplOracle.java 的addUser方法中加入新的逻辑,此时有两种方式:
1)写一个类userDaoImplOracle2.java来继承userDaoImplOracle.java,然后在userDaoImplOracle2.java的addUser方法中Override其父类的addUser方法,但使用继承使得与父类紧耦合:修改父类的话,也需要修改子类,且子类不能继承其他的类。
2)使用组合关系:userDaoImplOracle3.java:
在其中加入成员变量private UserDao userDao = new UserDaoImplOracle();
然后在其addUser方法中调用userDao.addUser方法。
6. 动态代理:AOP的底层实现原理
JDK的动态代理:目的是在某一层的系列的方法前执行..., 在方法后执行...
1)LogInterceptor作为实现了InvocationHandler的接口的类。
public class LogInterceptor implements InvocationHandler
{
private Object target;
public Object getTarget ()
{
return target;
}
public void setTarget (Object target)
{
this.target = target;
}
private void before (Method method)
{
System.out.println(method.getName() + " before...");
}
@Override
public Object invoke (Object proxy, Method method, Object[] args) throws Throwable
{
before(method);
method.invoke(target, args);
after(method);
return null;
}
private void after (Method method)
{
System.out.println(method.getName() + " after...");
}
}
2)这样调用:
@Test
public void test () throws Exception
{
UserDAO userDao = new UserDaoImplOracle();
LogInterceptor handler = new LogInterceptor();
handler.setTarget(userDao);
UserDAO userDaoProxy = (UserDAO)Proxy.newProxyInstance(
userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(),
handler);
userDaoProxy.addUser(new User());
}
这样,在执行userDaoProxy.addUser方法的时候,首先找到handler,然后执行其invoke方法。
分享到:
相关推荐
由于提供的文件内容中存在大量重复的网址信息,并没有实际的教学内容或者相关知识点,我将从标题“spring 学习”出发,结合描述“通过搭建基本的工程,从中学习spring的原理”来详细阐述Spring框架的相关知识点。...
以下是对"Spring学习资料大全"的详细解析: 1. **Spring框架基础**: - **依赖注入(Dependency Injection,DI)**:Spring的核心特性之一,它允许开发者在运行时通过XML配置或注解方式来管理对象间的依赖关系,...
本资源集合围绕"spring学习.zip",提供了多本深入讲解Spring及其相关技术的电子书籍,旨在帮助读者深入理解和掌握Spring生态。 1. **《深入实践Spring Boot.陈韶健.pdf》**:这本书详细介绍了Spring Boot,它是...
在"spring学习"的资源包中,我们看到三个关于"第四章 Spring的基本用法"的PPT文件,分别是"第一次"、"第三次"和"第二次"。虽然顺序可能有些混乱,但我们可以从中提取出一系列关键知识点。 1. **依赖注入**:Spring...
spring学习笔记
这个"spring学习资料,精心总结,不可错过,速领!.zip"压缩包显然是为那些想要深入理解Spring框架的人准备的。以下是压缩包内可能包含的一些关键知识点,以及它们在实际开发中的应用和重要性: 1. **IoC...
Spring学习资料文档合集,包含 spring2.0-reference_RC2.1_zh_cn spring_reference_inchinese_m2 SpringGuide Spring基础教程 spring框架,技术详解及使用指导
本资料“Spring学习笔记&源码”是基于网易云课堂黑马程序员的Spring四天精通课程,旨在帮助学习者深入理解和实践Spring框架。 笔记部分可能会涵盖以下内容: 1. **Spring概述**:介绍Spring框架的历史、特点和主要...
spring学习文档 适合新手
这个"spring学习资料"压缩包包含了多个文档,可以帮助我们深入理解并掌握Spring的核心概念和技术。 首先,"spring2.0-reference_final_zh_cn.chm"是Spring 2.0的中文参考手册,对于初学者来说非常宝贵。它详细介绍...
Spring学习思维导图Spring学习思维导图Spring学习思维导图Spring学习思维导图Spring学习思维导图Spring学习思维导图Spring学习思维导图Spring学习思维导图Spring学习思维导图Spring学习思维导图Spring学习思维导图...
Spring学习笔记.xmind
【Spring学习手册】 Spring框架是Java开发中的一个核心组件,尤其在企业级应用开发中扮演着重要角色。它提供了一种全面的编程和配置模型,旨在简化开发过程并提高可测试性。本手册专为Spring的初学者设计,旨在帮助...
Spring 学习文档 Spring 是一个Java企业级应用程序开发框架,它提供了一个通用的应用程序开发架构,帮助开发者更快速、更高效地开发企业级应用程序。本文档记录了学习 Spring 的过程,包括 Spring 的基础知识、...
这份"Spring学习笔记+学习源码.zip"资源包含了深入学习Spring及其相关技术的知识点,以及实践代码,对提升Spring技能将大有裨益。 首先,我们来详细讨论Spring框架的主要组件和功能: 1. **依赖注入(Dependency ...
"超好的Spring学习资料"这个压缩包显然包含了深入理解并掌握Spring框架的关键资源,尤其是包含的《Spring in Action》这本书,是Spring学习的经典之作。 1. **Spring框架概述**:Spring是一个开源的Java平台,它...
根据提供的压缩包文件名,我们可以推测这是一个逐步学习Spring的系列笔记。从"Spring_day1"开始,可能涵盖了Spring的基础概念、环境搭建和基本配置。"Spring_day2"可能涉及了依赖注入和AOP的深入讲解。"Spring_day3...
上述提到的Spring学习路线涵盖了从基础到高级的多个方面,包括了IOC、AOP、JDBC模板的使用和事务管理等核心内容。掌握这些知识点对于高效开发高质量的Java后端应用至关重要。对于Java后台开发人员而言,深入学习和...