利用反射➕注解,实现简单版本的IOC。
1.先来说下简单说下什么是IOC?
IOC是反转控制 (Inversion Of Control)的缩写。
打个比方:
传统的:当我们需要一个对象的时候需要去new一个。
IOC:当我们需要对象时直接从对象工厂里获取就行了。
在打个比方:
传统的:当我们需要买一台适合自己的电脑时,我们需要自己购买零件,然后组装。
IOC: 我们直接去工厂拿,然后他直接给你一个已经装好的电脑给你。
2.利用反射➕注解自己实现简单版本的IOC
2.1实现之前先来看个图说个问题?
图中的private IUserDao userDao=new IUserDaoImpl();这句代码如果说在实现类比较少的情况下,还是没有太大问题,但是如果说有几百个、上千个实现类呢?那是不是就需要去new 这么多个IUserDaoImpl,但是我们只是需要去用这个IUserDaoImpl里的方法,所以这个new 100个跟new 1个的效果是一样的。
所以这个时候,就出来了一种思想,就是把这个IUserDaoImpl、UserServiceImpl提前放进容器,什么使用什么时候拿出来就行。
2.2下面请看三种不同的写法。
2.2.1第一种需要哪个类直接去添加哪个
在启动类里需要用哪个就添加哪个
这是bean工厂
2.2.2先在配置文件里把需要的类先配置好
- 添加配置文件
改一下bean工厂里的初始化容器的代码
在启动类里重新测试一下
2.2.3 直接扫描包把带@Bean注解的类,注入到工厂里去,然后使用@AutoWired注解在需要时使用。
第一步、需要先自定义定义两个注解@Bean、@AutoWired这两个注解干嘛用的呢,一个是代表这个类的是一个bean会在程序运行的后被加载到内存里面,在我们需要使用的时候可以使用@AutoWired拿出来用而不用我们自己去new了。如下代码所示:
package reflects.demo.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}
package reflects.demo.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoWired {
}
第二步、需要改变第二种的写法就是用一个递归去遍历项目编译后的所有class文件。然后把扫出来的路径的前缀清掉留下来一个reflects.demo.Annotation.Bean这样的,当我们有了全类名了,那这个类的所有东西我们都能通过反射拿到,但是在new出对象后是需要我们去装配的不然,这个对象是空的。如下代码所示:
package reflects.demo.reflect;
import reflects.demo.Annotation.AutoWired;
import reflects.demo.Annotation.Bean;
import reflects.demo.dao.IUserDao;
import reflects.demo.dao.Impl.IUserDaoImpl;
import reflects.demo.service.Impl.UserServiceImpl;
import reflects.demo.service.IUserService;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class ApplicationContext<T> {
//这个是一个存的容器起名叫bean工厂
private HashMap<Class, Object> beanFactory = new HashMap<Class, Object>();
private String filePath;
//这个是你new 好了之后拿的一个bean
public T getBean(Class cla) {
return (T) beanFactory.get(cla);
}
//这是通过直接添加的方式,需要什么添加什么
// public void initContext(){
// beanFactory.put(IUserDao.class,new IUserDaoImpl());
// beanFactory.put(IUserService.class,new UserServiceImpl());
// }
//这是通过配置文件的方式
public void initContext() {
InputStream resource = ApplicationContext.class.getClassLoader()
.getResourceAsStream("config/bean.config");
Properties properties = new Properties();
try {
properties.load(resource);
Set<Object> keys = properties.keySet();
for (Object key : keys) {
beanFactory.put(Class.forName(key.toString()),
Class.forName(properties.getProperty(key.toString())).newInstance());
}
} catch (Exception e) {
e.printStackTrace();
}
}
//加载全部的类的实例
public void initContextByAnnotation() {
filePath = ApplicationContext.class.getClassLoader().getResource("").getFile();
loadOne(new File(filePath));
assembleObject();
System.out.println(filePath);
}
//这一步是装配的过程就是你new好对象之后,需要把对应的属性给她装配上,不然是个空是没有用的
private void assembleObject(){
//遍历工厂拿到每一个对象
for (Map.Entry<Class,Object> entry : beanFactory.entrySet()){
Object obj = entry.getValue();
Class<?> aClass = obj.getClass();
//遍历每一个对象的属性
Field[] declaredFields = aClass.getDeclaredFields();
for (Field field : declaredFields){
//这个是判断这个字段上有没有这么一个AutoWired注解,我才给你注入,不然不注入
AutoWired anntation= field.getAnnotation(AutoWired.class);
if (anntation !=null){这个
//因为是暴力注入所以需要
field. setAccessible(true);
try {
//1.给这个属性设置值
//2.field.getType()获取这个属性的类型,他就是个Class,也就在bean工厂里的key,然后beanFactory.get(field.getType())就是取出对应的对象,给这个属性赋值。
field.set(obj,beanFactory.get(field.getType()));
}catch (IllegalAccessException e){
e.printStackTrace();
}
}
}
}
}
/**
加载一个文件夹的类@param fiLeParent
*/
private void loadOne(File fileParent) {
if (fileParent.isDirectory()) {
File[] childrenFiles = fileParent.listFiles();
if (childrenFiles == null || childrenFiles.length == 0) {
return;
}
for (File child : childrenFiles) {
if (child.isDirectory()) {
//如果是个文件夹就继续调用该方法
loadOne(child);
} else {
//通过文件路径转变成全类名,第一步把绝对路径部分去掉
String pathWithClass = child.getAbsolutePath().substring(filePath.length() - 1);
//迭中c1ass文件
if (pathWithClass.contains(".c1ass")) {
//去掉.class后缀,并且把\替换成.
String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");
try {
//这个上面的操作就是遍历每一个文件夹然后取出文件,然后到这里就是通过反射拿到这个类
Class<?> aClass = Class.forName(fullName);
//把非接口的类实例化放在map中,这里是判断他不是一个接口就new,是的化就不new因为接口不能实列化
if (!aClass.isInterface()) {
Bean annotation= aClass.getAnnotation(Bean.class);
if(annotation !=null){
//这里就是利用反射new对象
//这里写不好,因为抽象类什么的都没考虑
Object instance = aClass.newInstance();
if(aClass.getInterfaces().length > 0){
//如果有接口,才把他的key当作它的接口
beanFactory.put(aClass.getInterfaces()[0], instance);
}else {
//如果没接口,直接用自己的类存放实例
beanFactory.put(aClass, instance);
}
}
}
} catch (ClassNotFoundException | IllegalAccessException |
InstantiationException e) {
e.printStackTrace();
}
}
}
}
}
}
}
第三步、启动类里开始启动
package reflects;
import reflects.demo.dao.IUserDao;
import reflects.demo.dao.Impl.IUserDaoImpl;
import reflects.demo.reflect.ApplicationContext;
import reflects.demo.service.IUserService;
public class BootStrap {
public static void main(String[] args) {
// ApplicationContext applicationContext = new ApplicationContext();
// applicationContext.initContext();
// System.out.println(applicationContext.getBean(IUserDao.class));
// ApplicationContext applicationContext = new ApplicationContext();
// applicationContext.initContextByAnnotation();
// Object bean= applicationContext.getBean(IUserService.class);
// System.out.println(bean);
ApplicationContext applicationContext = new ApplicationContext();
//初始化容器
applicationContext.initContextByAnnotation();
//取出对象
IUserService userService=(IUserService) applicationContext.getBean(IUserService.class);
userService.login("sss");
}
}
下面补全剩余代码:
UserServiceImpl:
package reflects.demo.service.Impl;
import org.springframework.beans.factory.annotation.Autowired;
import reflects.demo.Annotation.AutoWired;
import reflects.demo.Annotation.Bean;
import reflects.demo.dao.IUserDao;
import reflects.demo.dao.Impl.IUserDaoImpl;
import reflects.demo.service.IUserService;
@Bean
public class UserServiceImpl implements IUserService {
// private IUserDao userDao=new IUserDaoImpl();
@AutoWired
private IUserDao userDao;
public void login(String name) {
userDao.findByName(name);
System.out.println("用户登陆了");
}
}
IUserService
package reflects.demo.service;
import reflects.demo.Annotation.Bean;
public interface IUserService {
void login(String name);
}
User类的
package reflects.demo.entity;
import java.io.Serializable;
public class User implements Serializable {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
}
IUserDao
package reflects.demo.dao;
import reflects.demo.Annotation.Bean;
import reflects.demo.entity.User;
public interface IUserDao {
User findByName(String name);
}
IUserDaoImpl
package reflects.demo.dao.Impl;
import reflects.demo.Annotation.Bean;
import reflects.demo.dao.IUserDao;
import reflects.demo.entity.User;
@Bean
public class IUserDaoImpl implements IUserDao {
public User findByName(String name) {
System.out.println("用户"+name+"登陆了");
return null;
}
}
项目目录结构:
总结:最后使用的时候自己的路径要注意一下,这个就是一个简单版本的IOC,可以打成一个包然后在另一个项目里直接使用,这里我就不展示了。
总结2:反射跟注解搭配在一起用才是真的绝配,本人学艺不精不喜勿喷。