下面是我对spring IOC和DI的理解
IOC就是控制反转:
将对象的创建的权利及对象的生命周期的管理过程交由Spring框架来处理
从此在开发过程中不再需要关注对象的创建和生命周期的管理
在需要时由Spring框架提供
这个由spring框架管理对象创建和生命周期的机制称之为控制反转
比如就是我们以前创建一个类是需要自己来手工来new来创建,但是我们用springIOC的话就可以让容器来帮我们自动创建,把我们所需要创建的类托管给spring容器
但是也不是所有的类都可以被容器托管只有在一个类上有以下注解才可以被托管:
@Controller 标注控制层组件
@Service 标注业务层组件
@Respostory 标注数据访问组件
@Component 通用注解
//这里是一个要托管的bean
@MyComponent//这里是我自己定义的注解功能和@Component一样
public class Helloworld {
public Helloworld() {
System.out.println("无参构造方法");
}
}
//这是一个容器配置类
@MyConfiguration//这是容器配置类注解只有有个这才会被扫描
@MyComponentScan(basePackages = {"com.yc.bean"})//这是指定容器所要扫描的包下的所有要托管bean下的字节码和子包 这里可以写多个只需用,隔开即可
public class MyAppConfig {
}
//这就是容器类
public class MyAnnotationConfigApplicationContext{
private Map<String,Object> map=new HashMap<String, Object>();//容器其实也就是一个map里面键就是类名首字母小写 值就是对象
//这里是为了方便测试把所有异常都抛出了
public MyAnnotationConfigApplicationContext(Class<?>...componentClasses) throws InstantiationException, IllegalAccessException, InvocationTargetException, IOException, ClassNotFoundException {
//通过构造方法来创建容器
register(componentClasses);
}
//处理IOC
private void register(Class<?>[] componentClasses) throws IllegalAccessException, InstantiationException, InvocationTargetException, IOException, ClassNotFoundException {
//可能是多个路径
if (componentClasses.length<=0||componentClasses==null){
return;
}
//可能有多个容器字节码
for (Class cl:componentClasses){
if (!cl.isAnnotationPresent(MyConfiguration.class)){
continue;
}
//如果没有填则就是这个容器类的包下的所有字节码和子包
String[] basePackages=getAppConfigBasePackages(cl);
if (cl.isAnnotationPresent(MyComponentScan.class)){
MyComponentScan mcs= (MyComponentScan) cl.getAnnotation(MyComponentScan.class);
if (mcs.basePackages()!=null&&mcs.basePackages().length>0){
//如果有则就是填写的值如com.yc.bean
basePackages=mcs.basePackages();
}
}
//处理@MyBean的情况
Object obj=cl.newInstance();//就是当前解析的MyAppconfig对象
handleAtMyBean(cl,obj);
//处理basePackage 基础包下的所有托管bean
for (String basePackage:basePackages){
scanPackageAndSubPackageClasses(basePackage);//这里是把所有的com.yc.bean下的字节码及其子包下的字节码全部扫描到
}
//继续处理所有托管Bean IOC操作完成
handleManagedBean();
}
}
//处理managedbeanclasses中所有class类 筛选出所有的@Component @Service @Repository的类 并实例化 存到beanMap中
private void handleManagedBean() throws InstantiationException, IllegalAccessException, InvocationTargetException {
for (Class c:managedBeanClasses){
if (c.isAnnotationPresent(MyConponent.class)){
saveManagedBean(c);
}else if (c.isAnnotationPresent(MyService.class)){
saveManagedBean(c);
}else if (c.isAnnotationPresent(MyRepository.class)){
saveManagedBean(c);
}else {
continue;
}
}
}
//把所有被托管的bean创建然后存到容器map中
private void saveManagedBean(Class c) throws IllegalAccessException, InstantiationException, InvocationTargetException {
Object o=c.newInstance();
handlePostConstruct(o,c);
String beanID=c.getSimpleName().substring(0,1).toLowerCase()+c.getSimpleName().substring(1);
map.put(beanID,o);
}
private void scanPackageAndSubPackageClasses(String basePackage) throws IOException, ClassNotFoundException {
String packagePath=basePackage.replaceAll("\\.","/");
System.out.println("扫描包路径:"+basePackage+",替换后:"+packagePath);
Enumeration<URL> files=Thread.currentThread().getContextClassLoader().getResources(packagePath);
while (files.hasMoreElements()){
URL url=files.nextElement();
System.out.println("配置的扫描路径为:"+url.getFile());
findClassesInPackages(url.getFile(),basePackage);//第二个参数 com.yc.bean;
}
}
//存所有要扫描的字节码
private Set<Class> managedBeanClasses=new HashSet<Class>();
//查找file 下面及子包所有的要托管的class,存到一个set中
private void findClassesInPackages(final String file, String basePackage) throws ClassNotFoundException {
File files= new File(file);
File[] classfile=files.listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.getName().endsWith(".class")||pathname.isDirectory();
}
});
if (classfile.length<=0||classfile==null){
return;
}
//循环所有的文件字节码得到子目录和字节码文件
for (File cf:classfile){
if (!cf.isDirectory()){
//如果是字节码则加入到set中
URL[] urls=new URL[]{};
URLClassLoader ucl=new URLClassLoader(urls);
Class c=ucl.loadClass(basePackage+"."+cf.getName().replaceAll(".class",""));
managedBeanClasses.add(c);
}else {
//如果是子目录则循环
basePackage+="."+cf.getName().substring(cf.getName().lastIndexOf("/")+1);
findClassesInPackages(cf.getAbsolutePath(),basePackage);
}
}
}
//这是处理Bean注解 注解在方法上,声明当前方法的返回值是一个由Spring管理的Bean,在类spring加载的时 候就会创建该Bean,相当于xml中bean标签中配置的单个bean对象其他注解
private void handleAtMyBean(Class cls,Object obj) throws InvocationTargetException, IllegalAccessException {
//1.获取cls中所有的方法
Method[] methods= cls.getDeclaredMethods();
for (Method m:methods){
//2.判断每个方法上是否有@MyBean注解
if (m.isAnnotationPresent(MyBean.class)){
//3.有则invoke它 它有返回值则把他存到map里面 方法名为键 返回值为值
Object o= m.invoke(obj,null);
//TODO:加入处理 @MyBean注解对应的方法所实例化的类中的@MyPostConstruct对应的方法
handlePostConstruct(o,o.getClass());//o是helloworld的对象 o.getclass是helloworld反射对象
map.put(m.getName(),o);
handlePreDestroy(o,o.getClass());
}
}
}
//在bean对应的类中 修饰某个方法 将该方法声明为销毁的方法,对象销毁之前调用的方法
private void handlePreDestroy(Object o, Class<?> aClass) {
Method[] methods= aClass.getDeclaredMethods();
for (Method m:methods){}
}
//在bean对应的类中 修饰某个方法 将该方法声明为初始化方法,对象创建之后立即执行
private void handlePostConstruct(Object o, Class<?> aClass) throws InvocationTargetException, IllegalAccessException {
Method[] methods= aClass.getDeclaredMethods();
for (Method m:methods){
if (m.isAnnotationPresent(MyPostConstruct.class)){
m.invoke(o,null);
}
}
}
//拿到所指定要扫描的包路径
private String[] getAppConfigBasePackages(Class cl) {
String[] path=new String[1];//这里不是固定的
path[0]=cl.getPackage().getName();
return path;
}
}
//测试类代码
public class Test {
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, IOException, ClassNotFoundException {
MyApplicationContext ac=new MyAnnotationConfigApplicationContext(MyAppConfig.class);
}
}
运行就会在控制台打印:“无参构造方法”.
这里我们就完成了IOC操作,当然真实的springIOC比这个要复杂的多实际上是先把所有的要托管的Bean得所有信息都先存到一个map中,因为还有spring懒加载问题.
DI就是依赖注入:
构造器注入和Setter方法注入
用构造器参数实现强制依赖,setter方法实现可选依赖
注解注入
只有map中托管的bean的方法或者属性上有以下注解的才会进行注入
@Autowired 按类型注入,注入的属性必须提供set方法
@Resource 按名称注入,属于J2EE范畴,不属于Spring框架
配合使用,强制要求按照id寻找bean
意思就是对map中托管的bean进行注入
//在进行完IOC后进行DI
handleDI(map);
//map中的每一个Bean中每一个方法上有@Autowired @Resource注解的方法实现di
private void handleDI(Map<String, Object> map) throws InvocationTargetException, IllegalAccessException {
Collection<Object> objectCollection=map.values();
for (Object obj:objectCollection){
Class clss=obj.getClass();
Method[] methods=clss.getDeclaredMethods();
for (Method m:methods){
if (m.isAnnotationPresent(MyAutowired.class)&&m.getName().startsWith("set")){
invokeAutowiredMethod(m,obj);
}else if (m.isAnnotationPresent(MyResourcce.class)&&m.getName().startsWith("set")){
invokeResource(m,obj);
}
}
}
}
//激活带有Resource的方法
private void invokeResource(Method m, Object obj) throws InvocationTargetException, IllegalAccessException {
//取出Resource中的name的值 当成beanID
MyResourcce mr=m.getAnnotation(MyResourcce.class);
String beanID=mr.name();
//如果name的值等于空 则取出参数类型然后把首字母小写当成beanID
if (beanID==null||beanID.equalsIgnoreCase("")){
String pname=m.getParameterTypes()[0].getSimpleName();//取简单名不然就是全路径
beanID=pname.substring(0,1).toLowerCase()+pname.substring(1);
}
//从map中拿到实例
Object o=map.get(beanID);
//激活
m.invoke(obj,o);
}
//激活带有Autowired的方法
private void invokeAutowiredMethod(Method m, Object obj) throws InvocationTargetException, IllegalAccessException {
//取出m的参数类型
Class c=m.getParameterTypes()[0];
//从map中循环所有的object
Set<String> keys=map.keySet();
for (String key:keys){
//取出所有的实例
Object o=map.get(key);
//判断他们的类型是否一样,如果一样则激活
Class[] interfaces= c.getInterfaces();
for (Class cs:interfaces){
if (cs==c){
m.invoke(obj,o);
break;
}
}
}
}
这样就完成了DI依赖注入
以上是个人理解。